blob: c02c4077c5c163afa920b6b7d5e8df24264925ae [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);
Prithvi Pai1dd18d22025-09-19 14:27:03 +0530520 if (ethdevice.empty())
521 {
522 lg2::error("Channel name does not exist for channel {CHANNEL}",
523 "CHANNEL", ethernetDefaultChannelNum);
524 return std::nullopt;
525 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700526 ipmi::DbusObjectInfo ethernetObj{};
527 boost::system::error_code ec = ipmi::getDbusObject(
528 ctx, ethernetIntf, networkRoot, ethdevice, ethernetObj);
529 if (ec.value())
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600530 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700531 return std::nullopt;
532 }
533 std::string service{};
534 ec = ipmi::getService(ctx, ethernetIntf, ethernetObj.first, service);
535 if (ec.value())
536 {
537 return std::nullopt;
538 }
539 std::string dhcpVal{};
540 ec = ipmi::getDbusProperty(ctx, service, ethernetObj.first, ethernetIntf,
541 "DHCPEnabled", dhcpVal);
542 if (ec.value())
543 {
544 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600545 }
546
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700547 return EthernetInterface::convertDHCPConfFromString(dhcpVal);
548}
549
550std::optional<bool> getDHCPOption(ipmi::Context::ptr& ctx,
551 const std::string& prop)
552{
George Liucfd7fa82024-01-22 11:38:29 +0800553 ipmi::ObjectTree objectTree;
554 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700555 {
556 return std::nullopt;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600557 }
558
George Liucfd7fa82024-01-22 11:38:29 +0800559 for (const auto& [path, serviceMap] : objectTree)
560 {
561 for (const auto& [service, object] : serviceMap)
562 {
563 bool value{};
564 if (ipmi::getDbusProperty(ctx, service, path, dhcpIntf, prop,
565 value))
566 {
567 return std::nullopt;
568 }
569
570 if (value)
571 {
572 return true;
573 }
574 }
575 }
576
577 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700578}
579
580bool setDHCPOption(ipmi::Context::ptr& ctx, std::string prop, bool value)
581{
George Liucfd7fa82024-01-22 11:38:29 +0800582 ipmi::ObjectTree objectTree;
583 if (ipmi::getAllDbusObjects(ctx, networkRoot, dhcpIntf, objectTree))
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700584 {
George Liucfd7fa82024-01-22 11:38:29 +0800585 return false;
Vernon Mauery6475b5c2023-07-27 14:52:51 -0700586 }
George Liucfd7fa82024-01-22 11:38:29 +0800587
588 for (const auto& [path, serviceMap] : objectTree)
589 {
590 for (const auto& [service, object] : serviceMap)
591 {
592 if (ipmi::setDbusProperty(ctx, service, path, dhcpIntf, prop,
593 value))
594 {
595 return false;
596 }
597 }
598 }
599
600 return true;
Deepak Kodihalli0b459552018-02-06 06:25:12 -0600601}
602
Tom Josephbe5eaa12017-07-12 19:54:44 +0530603} // namespace dcmi
Chris Austen1810bec2015-10-13 12:12:39 -0500604
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700605ipmi::RspType<uint16_t, // reserved
606 uint8_t, // exception actions
607 uint16_t, // power limit requested in watts
608 uint32_t, // correction time in milliseconds
609 uint16_t, // reserved
610 uint16_t // statistics sampling period in seconds
611 >
612 getPowerLimit(ipmi::Context::ptr ctx, uint16_t reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530613{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300614 if (!dcmi::isDCMIPowerMgmtSupported())
615 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700616 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300617 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700618 if (reserved)
Tom Josephb9d86f42017-07-26 18:03:47 +0530619 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700620 return ipmi::responseInvalidFieldRequest();
Tom Josephb9d86f42017-07-26 18:03:47 +0530621 }
622
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700623 std::optional<uint16_t> pcapValue = dcmi::getPcap(ctx);
624 std::optional<bool> pcapEnable = dcmi::getPcapEnabled(ctx);
Thang Trana95978d2025-06-04 10:39:15 +0700625 std::optional<uint32_t> pcapCorrectTime = dcmi::getPcapCorrectTime(ctx);
626 std::optional<uint16_t> pcapSamplPeriod = dcmi::getPcapSamplPeriod(ctx);
627 std::optional<std::string> pcapExceptAct = dcmi::getPcapExceptAction(ctx);
628
629 if (!pcapValue || !pcapEnable || !pcapCorrectTime || !pcapSamplPeriod ||
630 !pcapExceptAct)
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700631 {
632 return ipmi::responseUnspecifiedError();
633 }
634
635 constexpr uint16_t reserved1{};
636 constexpr uint16_t reserved2{};
Thang Trana95978d2025-06-04 10:39:15 +0700637 uint8_t exception;
638
639 std::string exceptAct = pcapExceptAct.value();
640
641 if (exceptAct == dcmi::DbusExceptAct::noAction)
642 {
643 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::NoAction);
644 }
645 else if (exceptAct == dcmi::DbusExceptAct::hardPowerOff)
646 {
647 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::HardPowerOff);
648 }
649 else if (exceptAct == dcmi::DbusExceptAct::logEventOnly)
650 {
651 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::LogEventOnly);
652 }
653 else if (exceptAct == dcmi::DbusExceptAct::oem)
654 {
655 exception = static_cast<uint8_t>(dcmi::ExceptActOptions::Oem02);
656 }
657 else
658 {
659 return ipmi::responseUnspecifiedError();
660 }
661
662 if (!pcapEnable.value())
Tom Josephb9d86f42017-07-26 18:03:47 +0530663 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700664 constexpr ipmi::Cc responseNoPowerLimitSet = 0x80;
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700665 return ipmi::response(responseNoPowerLimitSet, reserved1, exception,
Thang Trana95978d2025-06-04 10:39:15 +0700666 pcapValue.value(), pcapCorrectTime.value(),
667 reserved2, pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530668 }
Thang Trana95978d2025-06-04 10:39:15 +0700669
670 return ipmi::responseSuccess(reserved1, exception, pcapValue.value(),
671 pcapCorrectTime.value(), reserved2,
672 pcapSamplPeriod.value());
Tom Josephb9d86f42017-07-26 18:03:47 +0530673}
674
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700675ipmi::RspType<> setPowerLimit(ipmi::Context::ptr& ctx, uint16_t reserved1,
Thang Tranfd9c3612023-09-20 11:16:59 +0700676 uint8_t reserved2, uint8_t exceptionAction,
677 uint16_t powerLimit, uint32_t correctionTime,
678 uint16_t reserved3, uint16_t statsPeriod)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530679{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300680 if (!dcmi::isDCMIPowerMgmtSupported())
681 {
George Liu7fa28712024-07-17 19:26:23 +0800682 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700683 return ipmi::responseInvalidCommand();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300684 }
685
Thang Trana95978d2025-06-04 10:39:15 +0700686 if (reserved1 || reserved2 || reserved3)
Tom Joseph46fa37d2017-07-26 18:11:55 +0530687 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700688 return ipmi::responseInvalidFieldRequest();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530689 }
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700690
691 if (!dcmi::setPcap(ctx, powerLimit))
Tom Joseph46fa37d2017-07-26 18:11:55 +0530692 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700693 return ipmi::responseUnspecifiedError();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530694 }
695
Thang Trana95978d2025-06-04 10:39:15 +0700696 /*
697 * As defined in table 6-18 of DCMI specification version 1.5.
698 * The Exception action value is mapped as below
699 * 00h - No Action
700 * 01h - Hard Power Off system and log events to SEL
701 * 02h - 10h OEM defined actions
702 * 11h - Log event to SEL only
703 * 12h-FFh Reserved
704 */
705 if (exceptionAction >= 0x12)
706 {
707 return ipmi::responseUnspecifiedError();
708 }
709
710 std::string exceptActStr;
711
712 switch (static_cast<dcmi::ExceptActOptions>(exceptionAction))
713 {
714 case dcmi::ExceptActOptions::NoAction:
715 {
716 exceptActStr = dcmi::DbusExceptAct::noAction;
717 break;
718 }
719 case dcmi::ExceptActOptions::HardPowerOff:
720 {
721 exceptActStr = dcmi::DbusExceptAct::hardPowerOff;
722 break;
723 }
724 case dcmi::ExceptActOptions::LogEventOnly:
725 {
726 exceptActStr = dcmi::DbusExceptAct::logEventOnly;
727 break;
728 }
729 default:
730 {
731 exceptActStr = dcmi::DbusExceptAct::oem;
732 break;
733 }
734 }
735
736 if (!dcmi::setPcapExceptAction(ctx, exceptActStr))
737 {
738 return ipmi::responseUnspecifiedError();
739 }
740
741 if (!dcmi::setPcapCorrectTime(ctx, correctionTime))
742 {
743 return ipmi::responseUnspecifiedError();
744 }
745
746 if (!dcmi::setPcapSamplPeriod(ctx, statsPeriod))
747 {
748 return ipmi::responseUnspecifiedError();
749 }
Tom Joseph46fa37d2017-07-26 18:11:55 +0530750
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700751 return ipmi::responseSuccess();
Tom Joseph46fa37d2017-07-26 18:11:55 +0530752}
753
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700754ipmi::RspType<> applyPowerLimit(ipmi::Context::ptr& ctx, bool enabled,
755 uint7_t reserved1, uint16_t reserved2)
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530756{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300757 if (!dcmi::isDCMIPowerMgmtSupported())
758 {
George Liu7fa28712024-07-17 19:26:23 +0800759 lg2::error("DCMI Power management is unsupported!");
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700760 return ipmi::responseInvalidCommand();
761 }
762 if (reserved1 || reserved2)
763 {
764 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +0300765 }
766
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700767 if (!dcmi::setPcapEnable(ctx, enabled))
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530768 {
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700769 return ipmi::responseUnspecifiedError();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530770 }
771
George Liu7fa28712024-07-17 19:26:23 +0800772 lg2::info("Set Power Cap Enable: {POWERCAPENABLE}", "POWERCAPENABLE",
773 enabled);
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530774
Vernon Maueryd4222fd2023-07-27 11:26:51 -0700775 return ipmi::responseSuccess();
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530776}
777
Vernon Mauerydca47202023-07-27 11:32:01 -0700778ipmi::RspType<uint8_t, // total tag length
779 std::vector<char> // tag data
780 >
781 getAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530782{
Vernon Mauerydca47202023-07-27 11:32:01 -0700783 // Verify offset to read and number of bytes to read are not exceeding
784 // the range.
785 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
786 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530787 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700788 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530789 }
790
Vernon Mauerydca47202023-07-27 11:32:01 -0700791 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
792 if (!assetTagResp)
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530793 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700794 return ipmi::responseUnspecifiedError();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530795 }
796
Vernon Mauerydca47202023-07-27 11:32:01 -0700797 std::string& assetTag = assetTagResp.value();
798 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to
799 // suit Get Asset Tag command.
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530800 if (assetTag.size() > dcmi::assetTagMaxSize)
801 {
802 assetTag.resize(dcmi::assetTagMaxSize);
803 }
804
Vernon Mauerydca47202023-07-27 11:32:01 -0700805 if (offset >= assetTag.size())
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530806 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700807 return ipmi::responseParmOutOfRange();
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530808 }
809
Vernon Mauerydca47202023-07-27 11:32:01 -0700810 // silently truncate reads beyond the end of assetTag
811 if ((offset + count) >= assetTag.size())
812 {
813 count = assetTag.size() - offset;
814 }
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530815
Vernon Mauerydca47202023-07-27 11:32:01 -0700816 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
817 std::vector<char> data{assetTag.begin() + offset,
818 assetTag.begin() + offset + count};
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530819
Vernon Mauerydca47202023-07-27 11:32:01 -0700820 return ipmi::responseSuccess(totalTagSize, data);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530821}
822
Vernon Mauerydca47202023-07-27 11:32:01 -0700823ipmi::RspType<uint8_t // new asset tag length
824 >
825 setAssetTag(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count,
826 const std::vector<char>& data)
Tom Joseph545dd232017-07-12 20:20:49 +0530827{
Vernon Mauerydca47202023-07-27 11:32:01 -0700828 // Verify offset to read and number of bytes to read are not exceeding
829 // the range.
830 if ((offset > dcmi::assetTagMaxOffset) || (count > dcmi::maxBytes) ||
831 ((offset + count) > dcmi::assetTagMaxSize))
Tom Joseph545dd232017-07-12 20:20:49 +0530832 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700833 return ipmi::responseParmOutOfRange();
834 }
835 if (data.size() != count)
836 {
837 return ipmi::responseReqDataLenInvalid();
Tom Joseph545dd232017-07-12 20:20:49 +0530838 }
839
Vernon Mauerydca47202023-07-27 11:32:01 -0700840 std::optional<std::string> assetTagResp = dcmi::readAssetTag(ctx);
841 if (!assetTagResp)
Tom Joseph545dd232017-07-12 20:20:49 +0530842 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700843 return ipmi::responseUnspecifiedError();
Tom Joseph545dd232017-07-12 20:20:49 +0530844 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700845
846 std::string& assetTag = assetTagResp.value();
847
848 if (offset > assetTag.size())
Tom Joseph545dd232017-07-12 20:20:49 +0530849 {
Vernon Mauerydca47202023-07-27 11:32:01 -0700850 return ipmi::responseParmOutOfRange();
Tom Joseph545dd232017-07-12 20:20:49 +0530851 }
Vernon Mauerydca47202023-07-27 11:32:01 -0700852
853 // operation is to truncate at offset and append new data
854 assetTag.resize(offset);
855 assetTag.append(data.begin(), data.end());
856
857 if (!dcmi::writeAssetTag(ctx, assetTag))
858 {
859 return ipmi::responseUnspecifiedError();
860 }
861
862 auto totalTagSize = static_cast<uint8_t>(assetTag.size());
863 return ipmi::responseSuccess(totalTagSize);
Tom Joseph545dd232017-07-12 20:20:49 +0530864}
865
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700866ipmi::RspType<uint8_t, // length
867 std::vector<char> // data
868 >
869 getMgmntCtrlIdStr(ipmi::Context::ptr& ctx, uint8_t offset, uint8_t count)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300870{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700871 if (count > dcmi::maxBytes || offset + count > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300872 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700873 return ipmi::responseParmOutOfRange();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300874 }
875
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700876 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
877 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300878 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700879 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300880 }
881
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700882 std::string& hostname = hostnameResp.value();
883 // If the id string is longer than 63 bytes, restrict it to 63 bytes to
884 // suit set management ctrl str command.
885 if (hostname.size() > dcmi::maxCtrlIdStrLen)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300886 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700887 hostname.resize(dcmi::maxCtrlIdStrLen);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300888 }
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300889
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700890 if (offset >= hostname.size())
891 {
892 return ipmi::responseParmOutOfRange();
893 }
894
895 // silently truncate reads beyond the end of hostname
896 if ((offset + count) >= hostname.size())
897 {
898 count = hostname.size() - offset;
899 }
900
901 auto nameSize = static_cast<uint8_t>(hostname.size());
902 std::vector<char> data{hostname.begin() + offset,
903 hostname.begin() + offset + count};
904
905 return ipmi::responseSuccess(nameSize, data);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300906}
907
Patrick Williams69b4c282025-03-03 11:19:13 -0500908ipmi::RspType<uint8_t> setMgmntCtrlIdStr(ipmi::Context::ptr& ctx,
909 uint8_t offset, uint8_t count,
910 std::vector<char> data)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300911{
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700912 if ((offset > dcmi::maxCtrlIdStrLen) || (count > dcmi::maxBytes) ||
913 ((offset + count) > dcmi::maxCtrlIdStrLen))
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300914 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700915 return ipmi::responseParmOutOfRange();
916 }
917 if (data.size() != count)
918 {
919 return ipmi::responseReqDataLenInvalid();
920 }
921 bool terminalWrite{data.back() == '\0'};
922 if (terminalWrite)
923 {
924 // remove the null termination from the data (no need with std::string)
925 data.resize(count - 1);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300926 }
927
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700928 static std::string hostname{};
929 // read in the current value if not starting at offset 0
930 if (hostname.size() == 0 && offset != 0)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300931 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700932 /* read old ctrlIdStr */
933 std::optional<std::string> hostnameResp = dcmi::getHostName(ctx);
934 if (!hostnameResp)
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300935 {
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700936 return ipmi::responseUnspecifiedError();
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300937 }
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700938 hostname = hostnameResp.value();
939 hostname.resize(offset);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300940 }
941
Vernon Maueryefb5ae52023-07-27 11:35:43 -0700942 // operation is to truncate at offset and append new data
943 hostname.append(data.begin(), data.end());
944
945 // do the update if this is the last write
946 if (terminalWrite)
947 {
948 boost::system::error_code ec = ipmi::setDbusProperty(
949 ctx, dcmi::networkServiceName, dcmi::networkConfigObj,
950 dcmi::networkConfigIntf, dcmi::hostNameProp, hostname);
951 hostname.clear();
952 if (ec.value())
953 {
954 return ipmi::responseUnspecifiedError();
955 }
956 }
957
958 auto totalIdSize = static_cast<uint8_t>(offset + count);
959 return ipmi::responseSuccess(totalIdSize);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300960}
961
Vernon Maueryf038dc02023-07-27 14:06:11 -0700962ipmi::RspType<ipmi::message::Payload> getDCMICapabilities(uint8_t parameter)
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600963{
Kirill Pakhomova2573622018-11-02 19:00:18 +0300964 std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600965 if (!dcmiCapFile.is_open())
966 {
George Liu7fa28712024-07-17 19:26:23 +0800967 lg2::error("DCMI Capabilities file not found");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700968 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600969 }
970
971 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
972 if (data.is_discarded())
973 {
George Liu7fa28712024-07-17 19:26:23 +0800974 lg2::error("DCMI Capabilities JSON parser failure");
Vernon Maueryf038dc02023-07-27 14:06:11 -0700975 return ipmi::responseUnspecifiedError();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600976 }
977
Vernon Maueryf038dc02023-07-27 14:06:11 -0700978 constexpr bool reserved1{};
979 constexpr uint5_t reserved5{};
980 constexpr uint7_t reserved7{};
981 constexpr uint8_t reserved8{};
982 constexpr uint16_t reserved16{};
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600983
Vernon Maueryf038dc02023-07-27 14:06:11 -0700984 ipmi::message::Payload payload;
985 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
986 dcmi::parameterRevision);
987
988 enum class DCMICapParameters : uint8_t
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600989 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700990 SupportedDcmiCaps = 0x01, // Supported DCMI Capabilities
991 MandatoryPlatAttributes = 0x02, // Mandatory Platform Attributes
992 OptionalPlatAttributes = 0x03, // Optional Platform Attributes
993 ManageabilityAccessAttributes = 0x04, // Manageability Access Attributes
994 };
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600995
Vernon Maueryf038dc02023-07-27 14:06:11 -0700996 switch (static_cast<DCMICapParameters>(parameter))
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600997 {
Vernon Maueryf038dc02023-07-27 14:06:11 -0700998 case DCMICapParameters::SupportedDcmiCaps:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600999 {
Vernon Maueryf038dc02023-07-27 14:06:11 -07001000 bool powerManagement = data.value("PowerManagement", 0);
1001 bool oobSecondaryLan = data.value("OOBSecondaryLan", 0);
1002 bool serialTMode = data.value("SerialTMODE", 0);
1003 bool inBandSystemInterfaceChannel =
1004 data.value("InBandSystemInterfaceChannel", 0);
1005 payload.pack(reserved8, powerManagement, reserved7,
1006 inBandSystemInterfaceChannel, serialTMode,
1007 oobSecondaryLan, reserved5);
1008 break;
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001009 }
Vernon Maueryf038dc02023-07-27 14:06:11 -07001010 // Mandatory Platform Attributes
1011 case DCMICapParameters::MandatoryPlatAttributes:
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001012 {
Vernon Maueryf038dc02023-07-27 14:06:11 -07001013 bool selAutoRollOver = data.value("SELAutoRollOver", 0);
1014 bool flushEntireSELUponRollOver =
1015 data.value("FlushEntireSELUponRollOver", 0);
1016 bool recordLevelSELFlushUponRollOver =
1017 data.value("RecordLevelSELFlushUponRollOver", 0);
Patrick Williams1318a5e2024-08-16 15:19:54 -04001018 uint12_t numberOfSELEntries =
1019 data.value("NumberOfSELEntries", 0xcac);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001020 uint8_t tempMonitoringSamplingFreq =
1021 data.value("TempMonitoringSamplingFreq", 0);
1022 payload.pack(numberOfSELEntries, reserved1,
1023 recordLevelSELFlushUponRollOver,
1024 flushEntireSELUponRollOver, selAutoRollOver,
1025 reserved16, tempMonitoringSamplingFreq);
1026 break;
1027 }
1028 // Optional Platform Attributes
1029 case DCMICapParameters::OptionalPlatAttributes:
1030 {
Matt Simmering68d9d402023-11-09 14:22:11 -08001031 uint7_t powerMgmtDeviceTargetAddress =
Vernon Maueryf038dc02023-07-27 14:06:11 -07001032 data.value("PowerMgmtDeviceSlaveAddress", 0);
1033 uint4_t bmcChannelNumber = data.value("BMCChannelNumber", 0);
1034 uint4_t deviceRivision = data.value("DeviceRivision", 0);
Matt Simmering68d9d402023-11-09 14:22:11 -08001035 payload.pack(powerMgmtDeviceTargetAddress, reserved1,
1036 deviceRivision, bmcChannelNumber);
Vernon Maueryf038dc02023-07-27 14:06:11 -07001037 break;
1038 }
1039 // Manageability Access Attributes
1040 case DCMICapParameters::ManageabilityAccessAttributes:
1041 {
1042 uint8_t mandatoryPrimaryLanOOBSupport =
1043 data.value("MandatoryPrimaryLanOOBSupport", 0xff);
1044 uint8_t optionalSecondaryLanOOBSupport =
1045 data.value("OptionalSecondaryLanOOBSupport", 0xff);
1046 uint8_t optionalSerialOOBMTMODECapability =
1047 data.value("OptionalSerialOOBMTMODECapability", 0xff);
1048 payload.pack(mandatoryPrimaryLanOOBSupport,
1049 optionalSecondaryLanOOBSupport,
1050 optionalSerialOOBMTMODECapability);
1051 break;
1052 }
1053 default:
1054 {
George Liu7fa28712024-07-17 19:26:23 +08001055 lg2::error("Invalid input parameter");
Vernon Maueryf038dc02023-07-27 14:06:11 -07001056 return ipmi::responseInvalidFieldRequest();
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001057 }
1058 }
1059
Vernon Maueryf038dc02023-07-27 14:06:11 -07001060 return ipmi::responseSuccess(payload);
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001061}
1062
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001063namespace dcmi
1064{
1065namespace temp_readings
1066{
1067
Patrick Williams69b4c282025-03-03 11:19:13 -05001068std::tuple<bool, bool, uint8_t> readTemp(ipmi::Context::ptr& ctx,
1069 const std::string& dbusService,
1070 const std::string& dbusPath)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001071{
1072 // Read the temperature value from d-bus object. Need some conversion.
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001073 // As per the interface xyz.openbmc_project.Sensor.Value, the
1074 // temperature is an double and in degrees C. It needs to be scaled by
1075 // using the formula Value * 10^Scale. The ipmi spec has the temperature
1076 // as a uint8_t, with a separate single bit for the sign.
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001077
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001078 ipmi::PropertyMap result{};
1079 boost::system::error_code ec = ipmi::getAllDbusProperties(
1080 ctx, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value", result);
1081 if (ec.value())
1082 {
1083 return std::make_tuple(false, false, 0);
1084 }
Patrick Williams1318a5e2024-08-16 15:19:54 -04001085 auto temperature =
1086 std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value"));
James Feist9cc0ea52018-10-09 10:53:11 -07001087 double absTemp = std::abs(temperature);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001088
James Feist9cc0ea52018-10-09 10:53:11 -07001089 auto findFactor = result.find("Scale");
1090 double factor = 0.0;
1091 if (findFactor != result.end())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001092 {
Vernon Maueryf442e112019-04-09 11:44:36 -07001093 factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001094 }
James Feist9cc0ea52018-10-09 10:53:11 -07001095 double scale = std::pow(10, factor);
1096
1097 auto tempDegrees = absTemp * scale;
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001098 // Max absolute temp as per ipmi spec is 127.
1099 constexpr auto maxTemp = 127;
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001100 if (tempDegrees > maxTemp)
1101 {
1102 tempDegrees = maxTemp;
1103 }
1104
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001105 return std::make_tuple(true, (temperature < 0),
1106 static_cast<uint8_t>(tempDegrees));
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001107}
1108
Patrick Williams69b4c282025-03-03 11:19:13 -05001109std::tuple<std::vector<std::tuple<uint7_t, bool, uint8_t>>, uint8_t> read(
1110 ipmi::Context::ptr& ctx, const std::string& type, uint8_t instance,
1111 size_t count)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001112{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001113 std::vector<std::tuple<uint7_t, bool, uint8_t>> response{};
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001114
Kirill Pakhomova2573622018-11-02 19:00:18 +03001115 auto data = parseJSONConfig(gDCMISensorsConfig);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001116 static const std::vector<nlohmann::json> empty{};
1117 std::vector<nlohmann::json> readings = data.value(type, empty);
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001118 for (const auto& j : readings)
1119 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001120 // Max of 8 response data sets
1121 if (response.size() == count)
1122 {
1123 break;
1124 }
1125
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001126 uint8_t instanceNum = j.value("instance", 0);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001127 // Not in the instance range we're interested in
1128 if (instanceNum < instance)
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001129 {
1130 continue;
1131 }
1132
1133 std::string path = j.value("dbus", "");
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001134 std::string service{};
1135 boost::system::error_code ec = ipmi::getService(
1136 ctx, "xyz.openbmc_project.Sensor.Value", path, service);
1137 if (ec.value())
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001138 {
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001139 // not found on dbus
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001140 continue;
1141 }
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001142
1143 const auto& [ok, sign, temp] = readTemp(ctx, service, path);
1144 if (ok)
1145 {
1146 response.emplace_back(uint7_t{temp}, sign, instanceNum);
1147 }
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -06001148 }
1149
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001150 auto totalInstances =
1151 static_cast<uint8_t>(std::min(readings.size(), maxInstances));
1152 return std::make_tuple(response, totalInstances);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001153}
1154
Patrick Venture0b02be92018-08-31 11:55:55 -07001155} // namespace temp_readings
1156} // namespace dcmi
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001157
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001158ipmi::RspType<uint8_t, // total instances for entity id
1159 uint8_t, // number of instances in this reply
1160 std::vector< // zero or more of the following two bytes
1161 std::tuple<uint7_t, // temperature value
1162 bool, // sign bit
1163 uint8_t // entity instance
1164 >>>
1165 getTempReadings(ipmi::Context::ptr& ctx, uint8_t sensorType,
1166 uint8_t entityId, uint8_t entityInstance,
1167 uint8_t instanceStart)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001168{
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001169 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001170 if (it == dcmi::entityIdToName.end())
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001171 {
George Liu7fa28712024-07-17 19:26:23 +08001172 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001173 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001174 }
1175
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001176 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001177 {
George Liu7fa28712024-07-17 19:26:23 +08001178 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1179 sensorType);
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001180 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001181 }
1182
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001183 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001184
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001185 // Read requested instances
1186 const auto& [temps, totalInstances] = dcmi::temp_readings::read(
1187 ctx, it->second, instanceStart, requestedRecords);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001188
Vernon Mauerycce9ffd2023-07-27 14:15:17 -07001189 auto numInstances = static_cast<uint8_t>(temps.size());
1190
1191 return ipmi::responseSuccess(totalInstances, numInstances, temps);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001192}
1193
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001194ipmi::RspType<> setDCMIConfParams(ipmi::Context::ptr& ctx, uint8_t parameter,
1195 uint8_t setSelector,
1196 ipmi::message::Payload& payload)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001197{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001198 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001199 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001200 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001201 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001202 // Take action based on the Parameter Selector
1203 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001204 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001205 case dcmi::DCMIConfigParameters::ActivateDHCP:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001206 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001207 uint7_t reserved{};
1208 bool activate{};
1209 if (payload.unpack(activate, reserved) || !payload.fullyUnpacked())
1210 {
1211 return ipmi::responseReqDataLenInvalid();
1212 }
1213 if (reserved)
1214 {
1215 return ipmi::responseInvalidFieldRequest();
1216 }
1217 std::optional<EthernetInterface::DHCPConf> dhcpEnabled =
1218 dcmi::getDHCPEnabled(ctx);
1219 if (!dhcpEnabled)
1220 {
1221 return ipmi::responseUnspecifiedError();
1222 }
1223 if (activate &&
1224 (dhcpEnabled.value() != EthernetInterface::DHCPConf::none))
1225 {
1226 // When these conditions are met we have to trigger DHCP
1227 // protocol restart using the latest parameter settings,
1228 // but as per n/w manager design, each time when we
1229 // update n/w parameters, n/w service is restarted. So
1230 // we no need to take any action in this case.
1231 }
1232 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001233 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001234 case dcmi::DCMIConfigParameters::DiscoveryConfig:
1235 {
1236 bool option12{};
1237 uint6_t reserved1{};
1238 bool randBackOff{};
1239 if (payload.unpack(option12, reserved1, randBackOff) ||
1240 !payload.fullyUnpacked())
1241 {
1242 return ipmi::responseReqDataLenInvalid();
1243 }
1244 // Systemd-networkd doesn't support Random Back off
1245 if (reserved1 || randBackOff)
1246 {
1247 return ipmi::responseInvalidFieldRequest();
1248 }
1249 dcmi::setDHCPOption(ctx, dcmi::dhcpOpt12Enabled, option12);
1250 break;
1251 }
1252 // Systemd-networkd doesn't allow to configure DHCP timigs
1253 case dcmi::DCMIConfigParameters::DHCPTiming1:
1254 case dcmi::DCMIConfigParameters::DHCPTiming2:
1255 case dcmi::DCMIConfigParameters::DHCPTiming3:
1256 default:
1257 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001258 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001259 return ipmi::responseSuccess();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001260}
1261
Patrick Williams1318a5e2024-08-16 15:19:54 -04001262ipmi::RspType<ipmi::message::Payload> getDCMIConfParams(
1263 ipmi::Context::ptr& ctx, uint8_t parameter, uint8_t setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001264{
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001265 if (setSelector)
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001266 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001267 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001268 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001269 ipmi::message::Payload payload;
1270 payload.pack(dcmi::specMajorVersion, dcmi::specMinorVersion,
1271 dcmi::configParameterRevision);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001272
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001273 // Take action based on the Parameter Selector
1274 switch (static_cast<dcmi::DCMIConfigParameters>(parameter))
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001275 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001276 case dcmi::DCMIConfigParameters::ActivateDHCP:
1277 payload.pack(dcmi::activateDhcpReply);
1278 break;
1279 case dcmi::DCMIConfigParameters::DiscoveryConfig:
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001280 {
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001281 uint8_t discovery{};
1282 std::optional<bool> enabled =
1283 dcmi::getDHCPOption(ctx, dcmi::dhcpOpt12Enabled);
1284 if (!enabled.has_value())
1285 {
1286 return ipmi::responseUnspecifiedError();
1287 }
1288 if (enabled.value())
1289 {
1290 discovery = dcmi::option12Mask;
1291 }
1292 payload.pack(discovery);
1293 break;
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001294 }
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001295 // Get below values from Systemd-networkd source code
1296 case dcmi::DCMIConfigParameters::DHCPTiming1:
1297 payload.pack(dcmi::dhcpTiming1);
1298 break;
1299 case dcmi::DCMIConfigParameters::DHCPTiming2:
1300 payload.pack(dcmi::dhcpTiming2);
1301 break;
1302 case dcmi::DCMIConfigParameters::DHCPTiming3:
1303 payload.pack(dcmi::dhcpTiming3);
1304 break;
1305 default:
1306 return ipmi::responseInvalidFieldRequest();
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001307 }
1308
Thang Tranaf762de2023-12-18 11:19:28 +07001309 return ipmi::responseSuccess(payload);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001310}
1311
Vernon Mauery056fab12023-07-27 14:25:24 -07001312static std::optional<uint16_t> readPower(ipmi::Context::ptr& ctx)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001313{
Vernon Mauery056fab12023-07-27 14:25:24 -07001314 std::ifstream sensorFile(POWER_READING_SENSOR);
1315 std::string objectPath;
1316 if (!sensorFile.is_open())
1317 {
George Liu7fa28712024-07-17 19:26:23 +08001318 lg2::error(
1319 "Power reading configuration file not found: {POWER_SENSOR_FILE}",
1320 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001321 return std::nullopt;
1322 }
1323
1324 auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1325 if (data.is_discarded())
1326 {
George Liu7fa28712024-07-17 19:26:23 +08001327 lg2::error("Error in parsing configuration file: {POWER_SENSOR_FILE}",
1328 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001329 return std::nullopt;
1330 }
1331
1332 objectPath = data.value("path", "");
1333 if (objectPath.empty())
1334 {
George Liu7fa28712024-07-17 19:26:23 +08001335 lg2::error(
1336 "Power sensor D-Bus object path is empty: {POWER_SENSOR_FILE}",
1337 "POWER_SENSOR_FILE", std::string_view{POWER_READING_SENSOR});
Vernon Mauery056fab12023-07-27 14:25:24 -07001338 return std::nullopt;
1339 }
1340
1341 // Return default value if failed to read from D-Bus object
1342 std::string service{};
Patrick Williams1318a5e2024-08-16 15:19:54 -04001343 boost::system::error_code ec =
1344 ipmi::getService(ctx, dcmi::sensorValueIntf, objectPath, service);
Vernon Mauery056fab12023-07-27 14:25:24 -07001345 if (ec.value())
1346 {
George Liu7fa28712024-07-17 19:26:23 +08001347 lg2::error("Failed to fetch service for D-Bus object, "
1348 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1349 "OBJECT_PATH", objectPath, "INTERFACE",
1350 dcmi::sensorValueIntf);
Vernon Mauery056fab12023-07-27 14:25:24 -07001351 return std::nullopt;
1352 }
1353
1354 // Read the sensor value and scale properties
1355 double value{};
1356 ec = ipmi::getDbusProperty(ctx, service, objectPath, dcmi::sensorValueIntf,
1357 dcmi::sensorValueProp, value);
1358 if (ec.value())
1359 {
George Liu7fa28712024-07-17 19:26:23 +08001360 lg2::error("Failed to read power value from D-Bus object, "
1361 "object path: {OBJECT_PATH}, interface: {INTERFACE}",
1362 "OBJECT_PATH", objectPath, "INTERFACE",
1363 dcmi::sensorValueIntf);
Vernon Mauery056fab12023-07-27 14:25:24 -07001364 return std::nullopt;
1365 }
1366 auto power = static_cast<uint16_t>(value);
1367 return power;
1368}
1369
1370ipmi::RspType<uint16_t, // current power
1371 uint16_t, // minimum power
1372 uint16_t, // maximum power
1373 uint16_t, // average power
1374 uint32_t, // timestamp
1375 uint32_t, // sample period ms
1376 uint6_t, // reserved
1377 bool, // power measurement active
1378 bool // reserved
1379 >
1380 getPowerReading(ipmi::Context::ptr& ctx, uint8_t mode, uint8_t attributes,
1381 uint8_t reserved)
1382{
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001383 if (!dcmi::isDCMIPowerMgmtSupported())
1384 {
George Liu7fa28712024-07-17 19:26:23 +08001385 lg2::error("DCMI Power management is unsupported!");
Vernon Mauery056fab12023-07-27 14:25:24 -07001386 return ipmi::responseInvalidCommand();
1387 }
1388 if (reserved)
1389 {
1390 return ipmi::responseInvalidFieldRequest();
Kirill Pakhomov2c2af2c2018-11-06 16:06:10 +03001391 }
1392
Vernon Mauery056fab12023-07-27 14:25:24 -07001393 enum class PowerMode : uint8_t
1394 {
1395 SystemPowerStatistics = 1,
1396 EnhancedSystemPowerStatistics = 2,
1397 };
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001398
Vernon Mauery056fab12023-07-27 14:25:24 -07001399 if (static_cast<PowerMode>(mode) != PowerMode::SystemPowerStatistics)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001400 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001401 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001402 }
Vernon Mauery056fab12023-07-27 14:25:24 -07001403 if (attributes)
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001404 {
Vernon Mauery056fab12023-07-27 14:25:24 -07001405 return ipmi::responseInvalidFieldRequest();
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001406 }
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001407
Vernon Mauery056fab12023-07-27 14:25:24 -07001408 std::optional<uint16_t> powerResp = readPower(ctx);
1409 if (!powerResp)
1410 {
1411 return ipmi::responseUnspecifiedError();
1412 }
1413 auto& power = powerResp.value();
1414
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001415 // TODO: openbmc/openbmc#2819
Gunnar Mills8466b792018-03-23 12:18:12 -05001416 // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001417 // PowerReadingState readings need to be populated
1418 // after Telemetry changes.
Vernon Mauery056fab12023-07-27 14:25:24 -07001419 constexpr uint32_t samplePeriod = 1;
1420 constexpr uint6_t reserved1 = 0;
1421 constexpr bool measurementActive = true;
1422 constexpr bool reserved2 = false;
1423 auto timestamp = static_cast<uint32_t>(time(nullptr));
1424 return ipmi::responseSuccess(power, power, power, power, timestamp,
1425 samplePeriod, reserved1, measurementActive,
1426 reserved2);
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001427}
1428
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001429namespace dcmi
1430{
1431namespace sensor_info
1432{
1433
Patrick Williams69b4c282025-03-03 11:19:13 -05001434std::tuple<std::vector<uint16_t>, uint8_t> read(
1435 const std::string& type, uint8_t instance, const nlohmann::json& config,
1436 uint8_t count)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001437{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001438 std::vector<uint16_t> responses{};
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001439
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001440 static const std::vector<nlohmann::json> empty{};
1441 std::vector<nlohmann::json> readings = config.value(type, empty);
1442 uint8_t totalInstances = std::min(readings.size(), maxInstances);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001443 for (const auto& reading : readings)
1444 {
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001445 // limit to requested count
1446 if (responses.size() == count)
1447 {
1448 break;
1449 }
1450
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001451 uint8_t instanceNum = reading.value("instance", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001452 // Not in the instance range we're interested in
1453 if (instanceNum < instance)
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001454 {
1455 continue;
1456 }
1457
Thang Tran5ea83fa2023-10-16 14:37:56 +07001458 uint16_t recordId = reading.value("record_id", 0);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001459 responses.emplace_back(recordId);
Deepak Kodihallidd4cff12018-02-06 06:48:29 -06001460 }
1461
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001462 return std::make_tuple(responses, totalInstances);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001463}
1464
1465} // namespace sensor_info
1466} // namespace dcmi
1467
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001468ipmi::RspType<uint8_t, // total available instances
1469 uint8_t, // number of records in this response
1470 std::vector<uint16_t> // records
1471 >
1472 getSensorInfo(uint8_t sensorType, uint8_t entityId, uint8_t entityInstance,
1473 uint8_t instanceStart)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001474{
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001475 auto it = dcmi::entityIdToName.find(entityId);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001476 if (it == dcmi::entityIdToName.end())
1477 {
George Liu7fa28712024-07-17 19:26:23 +08001478 lg2::error("Unknown Entity ID: {ENTITY_ID}", "ENTITY_ID", entityId);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001479 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001480 }
1481
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001482 if (sensorType != dcmi::temperatureSensorType)
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001483 {
George Liu7fa28712024-07-17 19:26:23 +08001484 lg2::error("Invalid sensor type: {SENSOR_TYPE}", "SENSOR_TYPE",
1485 sensorType);
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001486 return ipmi::responseInvalidFieldRequest();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001487 }
1488
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001489 nlohmann::json config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001490
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001491 uint8_t requestedRecords = (entityInstance == 0) ? dcmi::maxRecords : 1;
1492 // Read requested instances
1493 const auto& [sensors, totalInstances] = dcmi::sensor_info::read(
1494 it->second, instanceStart, config, requestedRecords);
1495 uint8_t numRecords = sensors.size();
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001496
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001497 return ipmi::responseSuccess(totalInstances, numRecords, sensors);
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001498}
1499
George Liu5087b072025-03-11 19:28:17 +08001500void registerNetFnDcmiFunctions()
Chris Austen1810bec2015-10-13 12:12:39 -05001501{
Tom05732372016-09-06 17:21:23 +05301502 // <Get Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001503 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1504 ipmi::dcmi::cmdGetPowerLimit, ipmi::Privilege::User,
1505 getPowerLimit);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301506
Tom Joseph46fa37d2017-07-26 18:11:55 +05301507 // <Set Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001508 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1509 ipmi::dcmi::cmdSetPowerLimit,
1510 ipmi::Privilege::Operator, setPowerLimit);
Tom Joseph46fa37d2017-07-26 18:11:55 +05301511
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301512 // <Activate/Deactivate Power Limit>
Vernon Maueryd4222fd2023-07-27 11:26:51 -07001513 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1514 ipmi::dcmi::cmdActDeactivatePwrLimit,
1515 ipmi::Privilege::Operator, applyPowerLimit);
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301516
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301517 // <Get Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001518 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1519 ipmi::dcmi::cmdGetAssetTag, ipmi::Privilege::User,
1520 getAssetTag);
Tom Joseph545dd232017-07-12 20:20:49 +05301521
1522 // <Set Asset Tag>
Vernon Mauerydca47202023-07-27 11:32:01 -07001523 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1524 ipmi::dcmi::cmdSetAssetTag, ipmi::Privilege::Operator,
1525 setAssetTag);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001526
Gunnar Mills8991dd62017-10-25 17:11:29 -05001527 // <Get Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001528 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1529 ipmi::dcmi::cmdGetMgmtCntlrIdString,
1530 ipmi::Privilege::User, getMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001531
1532 // <Set Management Controller Identifier String>
Vernon Maueryefb5ae52023-07-27 11:35:43 -07001533 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1534 ipmi::dcmi::cmdSetMgmtCntlrIdString,
1535 ipmi::Privilege::Admin, setMgmntCtrlIdStr);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001536
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001537 // <Get DCMI capabilities>
Vernon Maueryf038dc02023-07-27 14:06:11 -07001538 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1539 ipmi::dcmi::cmdGetDcmiCapabilitiesInfo,
1540 ipmi::Privilege::User, getDCMICapabilities);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001541
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001542 // <Get Power Reading>
Vernon Mauery056fab12023-07-27 14:25:24 -07001543 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1544 ipmi::dcmi::cmdGetPowerReading, ipmi::Privilege::User,
1545 getPowerReading);
1546
adarshgrami042e9db2022-09-15 10:34:34 +05301547// The Get sensor should get the senor details dynamically when
1548// FEATURE_DYNAMIC_SENSORS is enabled.
1549#ifndef FEATURE_DYNAMIC_SENSORS
Deepak Kodihalli0b459552018-02-06 06:25:12 -06001550 // <Get Sensor Info>
Vernon Mauery53d0cf12023-07-27 14:32:44 -07001551 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1552 ipmi::dcmi::cmdGetDcmiSensorInfo,
1553 ipmi::Privilege::Operator, getSensorInfo);
Thang Tran3dad8262023-08-17 15:20:56 +07001554
1555 // <Get Temperature Readings>
1556 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1557 ipmi::dcmi::cmdGetTemperatureReadings,
1558 ipmi::Privilege::User, getTempReadings);
adarshgrami042e9db2022-09-15 10:34:34 +05301559#endif
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001560 // <Get DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001561 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1562 ipmi::dcmi::cmdGetDcmiConfigParameters,
1563 ipmi::Privilege::User, getDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001564
1565 // <Set DCMI Configuration Parameters>
Vernon Mauery6475b5c2023-07-27 14:52:51 -07001566 registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
1567 ipmi::dcmi::cmdSetDcmiConfigParameters,
1568 ipmi::Privilege::Admin, setDCMIConfParams);
Nagaraju Goruganti22be97b2018-02-07 01:19:59 -06001569
Chris Austen1810bec2015-10-13 12:12:39 -05001570 return;
1571}