1 #include "config.h"
2 
3 #include "dcmihandler.hpp"
4 
5 #include "user_channel/channel_layer.hpp"
6 
7 #include <bitset>
8 #include <cmath>
9 #include <fstream>
10 #include <ipmid/api.hpp>
11 #include <ipmid/utils.hpp>
12 #include <nlohmann/json.hpp>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/log.hpp>
15 #include <sdbusplus/bus.hpp>
16 #include <sdbusplus/message/types.hpp>
17 #include <xyz/openbmc_project/Common/error.hpp>
18 
19 using namespace phosphor::logging;
20 using InternalFailure =
21     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
22 
23 void register_netfn_dcmi_functions() __attribute__((constructor));
24 
25 constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
26 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
27 
28 constexpr auto POWER_CAP_PROP = "PowerCap";
29 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
30 
31 constexpr auto DCMI_PARAMETER_REVISION = 2;
32 constexpr auto DCMI_SPEC_MAJOR_VERSION = 1;
33 constexpr auto DCMI_SPEC_MINOR_VERSION = 5;
34 constexpr auto DCMI_CONFIG_PARAMETER_REVISION = 1;
35 constexpr auto DCMI_RAND_BACK_OFF_MASK = 0x80;
36 constexpr auto DCMI_OPTION_60_43_MASK = 0x02;
37 constexpr auto DCMI_OPTION_12_MASK = 0x01;
38 constexpr auto DCMI_ACTIVATE_DHCP_MASK = 0x01;
39 constexpr auto DCMI_ACTIVATE_DHCP_REPLY = 0x00;
40 constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE = 0x05;
41 constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x04;
42 constexpr auto DHCP_TIMING1 = 0x04;       // 4 sec
43 constexpr auto DHCP_TIMING2_UPPER = 0x00; // 2 min
44 constexpr auto DHCP_TIMING2_LOWER = 0x78;
45 constexpr auto DHCP_TIMING3_UPPER = 0x00; // 64 sec
46 constexpr auto DHCP_TIMING3_LOWER = 0x40;
47 // When DHCP Option 12 is enabled the string "SendHostName=true" will be
48 // added into n/w configuration file and the parameter
49 // SendHostNameEnabled will set to true.
50 constexpr auto DHCP_OPT12_ENABLED = "SendHostNameEnabled";
51 
52 constexpr auto SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
53 constexpr auto SENSOR_VALUE_PROP = "Value";
54 constexpr auto SENSOR_SCALE_PROP = "Scale";
55 
56 using namespace phosphor::logging;
57 
58 namespace dcmi
59 {
60 
61 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
62 static const std::map<uint8_t, std::string> entityIdToName{
63     {0x40, "inlet"}, {0x37, "inlet"},     {0x41, "cpu"},
64     {0x03, "cpu"},   {0x42, "baseboard"}, {0x07, "baseboard"}};
65 
66 bool isDCMIPowerMgmtSupported()
67 {
68     auto data = parseJSONConfig(gDCMICapabilitiesConfig);
69 
70     return (gDCMIPowerMgmtSupported == data.value(gDCMIPowerMgmtCapability, 0));
71 }
72 
73 uint32_t getPcap(sdbusplus::bus::bus& bus)
74 {
75     auto settingService = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
76 
77     auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH,
78                                       "org.freedesktop.DBus.Properties", "Get");
79 
80     method.append(PCAP_INTERFACE, POWER_CAP_PROP);
81     auto reply = bus.call(method);
82 
83     if (reply.is_method_error())
84     {
85         log<level::ERR>("Error in getPcap prop");
86         elog<InternalFailure>();
87     }
88     sdbusplus::message::variant<uint32_t> pcap;
89     reply.read(pcap);
90 
91     return std::get<uint32_t>(pcap);
92 }
93 
94 bool getPcapEnabled(sdbusplus::bus::bus& bus)
95 {
96     auto settingService = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
97 
98     auto method = bus.new_method_call(settingService.c_str(), PCAP_PATH,
99                                       "org.freedesktop.DBus.Properties", "Get");
100 
101     method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
102     auto reply = bus.call(method);
103 
104     if (reply.is_method_error())
105     {
106         log<level::ERR>("Error in getPcapEnabled prop");
107         elog<InternalFailure>();
108     }
109     sdbusplus::message::variant<bool> pcapEnabled;
110     reply.read(pcapEnabled);
111 
112     return std::get<bool>(pcapEnabled);
113 }
114 
115 void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
116 {
117     auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
118 
119     auto method = bus.new_method_call(service.c_str(), PCAP_PATH,
120                                       "org.freedesktop.DBus.Properties", "Set");
121 
122     method.append(PCAP_INTERFACE, POWER_CAP_PROP);
123     method.append(sdbusplus::message::variant<uint32_t>(powerCap));
124 
125     auto reply = bus.call(method);
126 
127     if (reply.is_method_error())
128     {
129         log<level::ERR>("Error in setPcap property");
130         elog<InternalFailure>();
131     }
132 }
133 
134 void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled)
135 {
136     auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
137 
138     auto method = bus.new_method_call(service.c_str(), PCAP_PATH,
139                                       "org.freedesktop.DBus.Properties", "Set");
140 
141     method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
142     method.append(sdbusplus::message::variant<bool>(enabled));
143 
144     auto reply = bus.call(method);
145 
146     if (reply.is_method_error())
147     {
148         log<level::ERR>("Error in setPcapEnabled property");
149         elog<InternalFailure>();
150     }
151 }
152 
153 void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree)
154 {
155     static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
156     static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
157     static constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
158     static constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory/";
159 
160     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
161     auto depth = 0;
162 
163     auto mapperCall = bus.new_method_call(mapperBusName, mapperObjPath,
164                                           mapperIface, "GetSubTree");
165 
166     mapperCall.append(inventoryRoot);
167     mapperCall.append(depth);
168     mapperCall.append(std::vector<std::string>({dcmi::assetTagIntf}));
169 
170     auto mapperReply = bus.call(mapperCall);
171     if (mapperReply.is_method_error())
172     {
173         log<level::ERR>("Error in mapper call");
174         elog<InternalFailure>();
175     }
176 
177     mapperReply.read(objectTree);
178 
179     if (objectTree.empty())
180     {
181         log<level::ERR>("AssetTag property is not populated");
182         elog<InternalFailure>();
183     }
184 }
185 
186 std::string readAssetTag()
187 {
188     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
189     dcmi::assettag::ObjectTree objectTree;
190 
191     // Read the object tree with the inventory root to figure out the object
192     // that has implemented the Asset tag interface.
193     readAssetTagObjectTree(objectTree);
194 
195     auto method = bus.new_method_call(
196         (objectTree.begin()->second.begin()->first).c_str(),
197         (objectTree.begin()->first).c_str(), dcmi::propIntf, "Get");
198     method.append(dcmi::assetTagIntf);
199     method.append(dcmi::assetTagProp);
200 
201     auto reply = bus.call(method);
202     if (reply.is_method_error())
203     {
204         log<level::ERR>("Error in reading asset tag");
205         elog<InternalFailure>();
206     }
207 
208     sdbusplus::message::variant<std::string> assetTag;
209     reply.read(assetTag);
210 
211     return std::get<std::string>(assetTag);
212 }
213 
214 void writeAssetTag(const std::string& assetTag)
215 {
216     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
217     dcmi::assettag::ObjectTree objectTree;
218 
219     // Read the object tree with the inventory root to figure out the object
220     // that has implemented the Asset tag interface.
221     readAssetTagObjectTree(objectTree);
222 
223     auto method = bus.new_method_call(
224         (objectTree.begin()->second.begin()->first).c_str(),
225         (objectTree.begin()->first).c_str(), dcmi::propIntf, "Set");
226     method.append(dcmi::assetTagIntf);
227     method.append(dcmi::assetTagProp);
228     method.append(sdbusplus::message::variant<std::string>(assetTag));
229 
230     auto reply = bus.call(method);
231     if (reply.is_method_error())
232     {
233         log<level::ERR>("Error in writing asset tag");
234         elog<InternalFailure>();
235     }
236 }
237 
238 std::string getHostName(void)
239 {
240     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
241 
242     auto service = ipmi::getService(bus, networkConfigIntf, networkConfigObj);
243     auto value = ipmi::getDbusProperty(bus, service, networkConfigObj,
244                                        networkConfigIntf, hostNameProp);
245 
246     return std::get<std::string>(value);
247 }
248 
249 bool getDHCPEnabled()
250 {
251     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
252 
253     auto ethdevice = ipmi::getChannelName(ethernetDefaultChannelNum);
254     auto ethernetObj =
255         ipmi::getDbusObject(bus, ethernetIntf, networkRoot, ethdevice);
256     auto service = ipmi::getService(bus, ethernetIntf, ethernetObj.first);
257     auto value = ipmi::getDbusProperty(bus, service, ethernetObj.first,
258                                        ethernetIntf, "DHCPEnabled");
259 
260     return std::get<bool>(value);
261 }
262 
263 bool getDHCPOption(std::string prop)
264 {
265     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
266 
267     auto service = ipmi::getService(bus, dhcpIntf, dhcpObj);
268     auto value = ipmi::getDbusProperty(bus, service, dhcpObj, dhcpIntf, prop);
269 
270     return std::get<bool>(value);
271 }
272 
273 void setDHCPOption(std::string prop, bool value)
274 {
275     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
276 
277     auto service = ipmi::getService(bus, dhcpIntf, dhcpObj);
278     ipmi::setDbusProperty(bus, service, dhcpObj, dhcpIntf, prop, value);
279 }
280 
281 Json parseJSONConfig(const std::string& configFile)
282 {
283     std::ifstream jsonFile(configFile);
284     if (!jsonFile.is_open())
285     {
286         log<level::ERR>("Temperature readings JSON file not found");
287         elog<InternalFailure>();
288     }
289 
290     auto data = Json::parse(jsonFile, nullptr, false);
291     if (data.is_discarded())
292     {
293         log<level::ERR>("Temperature readings JSON parser failure");
294         elog<InternalFailure>();
295     }
296 
297     return data;
298 }
299 
300 } // namespace dcmi
301 
302 ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
303                          ipmi_request_t request, ipmi_response_t response,
304                          ipmi_data_len_t data_len, ipmi_context_t context)
305 {
306     if (!dcmi::isDCMIPowerMgmtSupported())
307     {
308         *data_len = 0;
309         log<level::ERR>("DCMI Power management is unsupported!");
310         return IPMI_CC_INVALID;
311     }
312 
313     auto requestData =
314         reinterpret_cast<const dcmi::GetPowerLimitRequest*>(request);
315     std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
316     auto responseData =
317         reinterpret_cast<dcmi::GetPowerLimitResponse*>(outPayload.data());
318 
319     if (requestData->groupID != dcmi::groupExtId)
320     {
321         *data_len = 0;
322         return IPMI_CC_INVALID_FIELD_REQUEST;
323     }
324 
325     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
326     uint32_t pcapValue = 0;
327     bool pcapEnable = false;
328 
329     try
330     {
331         pcapValue = dcmi::getPcap(sdbus);
332         pcapEnable = dcmi::getPcapEnabled(sdbus);
333     }
334     catch (InternalFailure& e)
335     {
336         *data_len = 0;
337         return IPMI_CC_UNSPECIFIED_ERROR;
338     }
339 
340     responseData->groupID = dcmi::groupExtId;
341 
342     /*
343      * Exception action if power limit is exceeded and cannot be controlled
344      * with the correction time limit is hardcoded to Hard Power Off system
345      * and log event to SEL.
346      */
347     constexpr auto exception = 0x01;
348     responseData->exceptionAction = exception;
349 
350     responseData->powerLimit = static_cast<uint16_t>(pcapValue);
351 
352     /*
353      * Correction time limit and Statistics sampling period is currently not
354      * populated.
355      */
356 
357     *data_len = outPayload.size();
358     memcpy(response, outPayload.data(), *data_len);
359 
360     if (pcapEnable)
361     {
362         return IPMI_CC_OK;
363     }
364     else
365     {
366         return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT;
367     }
368 }
369 
370 ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
371                          ipmi_request_t request, ipmi_response_t response,
372                          ipmi_data_len_t data_len, ipmi_context_t context)
373 {
374     if (!dcmi::isDCMIPowerMgmtSupported())
375     {
376         *data_len = 0;
377         log<level::ERR>("DCMI Power management is unsupported!");
378         return IPMI_CC_INVALID;
379     }
380 
381     auto requestData =
382         reinterpret_cast<const dcmi::SetPowerLimitRequest*>(request);
383     std::vector<uint8_t> outPayload(sizeof(dcmi::SetPowerLimitResponse));
384     auto responseData =
385         reinterpret_cast<dcmi::SetPowerLimitResponse*>(outPayload.data());
386 
387     if (requestData->groupID != dcmi::groupExtId)
388     {
389         *data_len = 0;
390         return IPMI_CC_INVALID_FIELD_REQUEST;
391     }
392 
393     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
394 
395     // Only process the power limit requested in watts.
396     try
397     {
398         dcmi::setPcap(sdbus, requestData->powerLimit);
399     }
400     catch (InternalFailure& e)
401     {
402         *data_len = 0;
403         return IPMI_CC_UNSPECIFIED_ERROR;
404     }
405 
406     log<level::INFO>("Set Power Cap",
407                      entry("POWERCAP=%u", requestData->powerLimit));
408 
409     responseData->groupID = dcmi::groupExtId;
410     memcpy(response, outPayload.data(), outPayload.size());
411     *data_len = outPayload.size();
412 
413     return IPMI_CC_OK;
414 }
415 
416 ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
417                            ipmi_request_t request, ipmi_response_t response,
418                            ipmi_data_len_t data_len, ipmi_context_t context)
419 {
420     if (!dcmi::isDCMIPowerMgmtSupported())
421     {
422         *data_len = 0;
423         log<level::ERR>("DCMI Power management is unsupported!");
424         return IPMI_CC_INVALID;
425     }
426 
427     auto requestData =
428         reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>(request);
429     std::vector<uint8_t> outPayload(sizeof(dcmi::ApplyPowerLimitResponse));
430     auto responseData =
431         reinterpret_cast<dcmi::ApplyPowerLimitResponse*>(outPayload.data());
432 
433     if (requestData->groupID != dcmi::groupExtId)
434     {
435         *data_len = 0;
436         return IPMI_CC_INVALID_FIELD_REQUEST;
437     }
438 
439     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
440 
441     try
442     {
443         dcmi::setPcapEnable(sdbus,
444                             static_cast<bool>(requestData->powerLimitAction));
445     }
446     catch (InternalFailure& e)
447     {
448         *data_len = 0;
449         return IPMI_CC_UNSPECIFIED_ERROR;
450     }
451 
452     log<level::INFO>("Set Power Cap Enable",
453                      entry("POWERCAPENABLE=%u", requestData->powerLimitAction));
454 
455     responseData->groupID = dcmi::groupExtId;
456     memcpy(response, outPayload.data(), outPayload.size());
457     *data_len = outPayload.size();
458 
459     return IPMI_CC_OK;
460 }
461 
462 ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
463                        ipmi_request_t request, ipmi_response_t response,
464                        ipmi_data_len_t data_len, ipmi_context_t context)
465 {
466     auto requestData =
467         reinterpret_cast<const dcmi::GetAssetTagRequest*>(request);
468     std::vector<uint8_t> outPayload(sizeof(dcmi::GetAssetTagResponse));
469     auto responseData =
470         reinterpret_cast<dcmi::GetAssetTagResponse*>(outPayload.data());
471 
472     if (requestData->groupID != dcmi::groupExtId)
473     {
474         *data_len = 0;
475         return IPMI_CC_INVALID_FIELD_REQUEST;
476     }
477 
478     // Verify offset to read and number of bytes to read are not exceeding the
479     // range.
480     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
481         (requestData->bytes > dcmi::maxBytes) ||
482         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
483     {
484         *data_len = 0;
485         return IPMI_CC_PARM_OUT_OF_RANGE;
486     }
487 
488     std::string assetTag;
489 
490     try
491     {
492         assetTag = dcmi::readAssetTag();
493     }
494     catch (InternalFailure& e)
495     {
496         *data_len = 0;
497         return IPMI_CC_UNSPECIFIED_ERROR;
498     }
499 
500     responseData->groupID = dcmi::groupExtId;
501 
502     // Return if the asset tag is not populated.
503     if (!assetTag.size())
504     {
505         responseData->tagLength = 0;
506         memcpy(response, outPayload.data(), outPayload.size());
507         *data_len = outPayload.size();
508         return IPMI_CC_OK;
509     }
510 
511     // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit
512     // Get Asset Tag command.
513     if (assetTag.size() > dcmi::assetTagMaxSize)
514     {
515         assetTag.resize(dcmi::assetTagMaxSize);
516     }
517 
518     // If the requested offset is beyond the asset tag size.
519     if (requestData->offset >= assetTag.size())
520     {
521         *data_len = 0;
522         return IPMI_CC_PARM_OUT_OF_RANGE;
523     }
524 
525     auto returnData = assetTag.substr(requestData->offset, requestData->bytes);
526 
527     responseData->tagLength = assetTag.size();
528 
529     memcpy(response, outPayload.data(), outPayload.size());
530     memcpy(static_cast<uint8_t*>(response) + outPayload.size(),
531            returnData.data(), returnData.size());
532     *data_len = outPayload.size() + returnData.size();
533 
534     return IPMI_CC_OK;
535 }
536 
537 ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
538                        ipmi_request_t request, ipmi_response_t response,
539                        ipmi_data_len_t data_len, ipmi_context_t context)
540 {
541     auto requestData =
542         reinterpret_cast<const dcmi::SetAssetTagRequest*>(request);
543     std::vector<uint8_t> outPayload(sizeof(dcmi::SetAssetTagResponse));
544     auto responseData =
545         reinterpret_cast<dcmi::SetAssetTagResponse*>(outPayload.data());
546 
547     if (requestData->groupID != dcmi::groupExtId)
548     {
549         *data_len = 0;
550         return IPMI_CC_INVALID_FIELD_REQUEST;
551     }
552 
553     // Verify offset to read and number of bytes to read are not exceeding the
554     // range.
555     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
556         (requestData->bytes > dcmi::maxBytes) ||
557         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
558     {
559         *data_len = 0;
560         return IPMI_CC_PARM_OUT_OF_RANGE;
561     }
562 
563     std::string assetTag;
564 
565     try
566     {
567         assetTag = dcmi::readAssetTag();
568 
569         if (requestData->offset > assetTag.size())
570         {
571             *data_len = 0;
572             return IPMI_CC_PARM_OUT_OF_RANGE;
573         }
574 
575         assetTag.replace(requestData->offset,
576                          assetTag.size() - requestData->offset,
577                          static_cast<const char*>(request) +
578                              sizeof(dcmi::SetAssetTagRequest),
579                          requestData->bytes);
580 
581         dcmi::writeAssetTag(assetTag);
582 
583         responseData->groupID = dcmi::groupExtId;
584         responseData->tagLength = assetTag.size();
585         memcpy(response, outPayload.data(), outPayload.size());
586         *data_len = outPayload.size();
587 
588         return IPMI_CC_OK;
589     }
590     catch (InternalFailure& e)
591     {
592         *data_len = 0;
593         return IPMI_CC_UNSPECIFIED_ERROR;
594     }
595 }
596 
597 ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
598                              ipmi_request_t request, ipmi_response_t response,
599                              ipmi_data_len_t data_len, ipmi_context_t context)
600 {
601     auto requestData =
602         reinterpret_cast<const dcmi::GetMgmntCtrlIdStrRequest*>(request);
603     auto responseData =
604         reinterpret_cast<dcmi::GetMgmntCtrlIdStrResponse*>(response);
605     std::string hostName;
606 
607     *data_len = 0;
608 
609     if (requestData->groupID != dcmi::groupExtId ||
610         requestData->bytes > dcmi::maxBytes ||
611         requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen)
612     {
613         return IPMI_CC_INVALID_FIELD_REQUEST;
614     }
615 
616     try
617     {
618         hostName = dcmi::getHostName();
619     }
620     catch (InternalFailure& e)
621     {
622         return IPMI_CC_UNSPECIFIED_ERROR;
623     }
624 
625     if (requestData->offset > hostName.length())
626     {
627         return IPMI_CC_PARM_OUT_OF_RANGE;
628     }
629     auto responseStr = hostName.substr(requestData->offset, requestData->bytes);
630     auto responseStrLen = std::min(static_cast<std::size_t>(requestData->bytes),
631                                    responseStr.length() + 1);
632     responseData->groupID = dcmi::groupExtId;
633     responseData->strLen = hostName.length();
634     std::copy(begin(responseStr), end(responseStr), responseData->data);
635 
636     *data_len = sizeof(*responseData) + responseStrLen;
637     return IPMI_CC_OK;
638 }
639 
640 ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
641                              ipmi_request_t request, ipmi_response_t response,
642                              ipmi_data_len_t data_len, ipmi_context_t context)
643 {
644     static std::array<char, dcmi::maxCtrlIdStrLen + 1> newCtrlIdStr;
645 
646     auto requestData =
647         reinterpret_cast<const dcmi::SetMgmntCtrlIdStrRequest*>(request);
648     auto responseData =
649         reinterpret_cast<dcmi::SetMgmntCtrlIdStrResponse*>(response);
650 
651     *data_len = 0;
652 
653     if (requestData->groupID != dcmi::groupExtId ||
654         requestData->bytes > dcmi::maxBytes ||
655         requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 ||
656         (requestData->offset + requestData->bytes ==
657              dcmi::maxCtrlIdStrLen + 1 &&
658          requestData->data[requestData->bytes - 1] != '\0'))
659     {
660         return IPMI_CC_INVALID_FIELD_REQUEST;
661     }
662 
663     try
664     {
665         /* if there is no old value and offset is not 0 */
666         if (newCtrlIdStr[0] == '\0' && requestData->offset != 0)
667         {
668             /* read old ctrlIdStr */
669             auto hostName = dcmi::getHostName();
670             hostName.resize(dcmi::maxCtrlIdStrLen);
671             std::copy(begin(hostName), end(hostName), begin(newCtrlIdStr));
672             newCtrlIdStr[hostName.length()] = '\0';
673         }
674 
675         /* replace part of string and mark byte after the last as \0 */
676         auto restStrIter =
677             std::copy_n(requestData->data, requestData->bytes,
678                         begin(newCtrlIdStr) + requestData->offset);
679         /* if the last written byte is not 64th - add '\0' */
680         if (requestData->offset + requestData->bytes <= dcmi::maxCtrlIdStrLen)
681         {
682             *restStrIter = '\0';
683         }
684 
685         /* if input data contains '\0' whole string is sent - update hostname */
686         auto it = std::find(requestData->data,
687                             requestData->data + requestData->bytes, '\0');
688         if (it != requestData->data + requestData->bytes)
689         {
690             sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
691             ipmi::setDbusProperty(bus, dcmi::networkServiceName,
692                                   dcmi::networkConfigObj,
693                                   dcmi::networkConfigIntf, dcmi::hostNameProp,
694                                   std::string(newCtrlIdStr.data()));
695         }
696     }
697     catch (InternalFailure& e)
698     {
699         *data_len = 0;
700         return IPMI_CC_UNSPECIFIED_ERROR;
701     }
702 
703     responseData->groupID = dcmi::groupExtId;
704     responseData->offset = requestData->offset + requestData->bytes;
705     *data_len = sizeof(*responseData);
706     return IPMI_CC_OK;
707 }
708 
709 // List of the capabilities under each parameter
710 dcmi::DCMICaps dcmiCaps = {
711     // Supported DCMI Capabilities
712     {dcmi::DCMICapParameters::SUPPORTED_DCMI_CAPS,
713      {3,
714       {{"PowerManagement", 2, 0, 1},
715        {"OOBSecondaryLan", 3, 2, 1},
716        {"SerialTMODE", 3, 1, 1},
717        {"InBandSystemInterfaceChannel", 3, 0, 1}}}},
718     // Mandatory Platform Attributes
719     {dcmi::DCMICapParameters::MANDATORY_PLAT_ATTRIBUTES,
720      {5,
721       {{"SELAutoRollOver", 1, 15, 1},
722        {"FlushEntireSELUponRollOver", 1, 14, 1},
723        {"RecordLevelSELFlushUponRollOver", 1, 13, 1},
724        {"NumberOfSELEntries", 1, 0, 12},
725        {"TempMonitoringSamplingFreq", 5, 0, 8}}}},
726     // Optional Platform Attributes
727     {dcmi::DCMICapParameters::OPTIONAL_PLAT_ATTRIBUTES,
728      {2,
729       {{"PowerMgmtDeviceSlaveAddress", 1, 1, 7},
730        {"BMCChannelNumber", 2, 4, 4},
731        {"DeviceRivision", 2, 0, 4}}}},
732     // Manageability Access Attributes
733     {dcmi::DCMICapParameters::MANAGEABILITY_ACCESS_ATTRIBUTES,
734      {3,
735       {{"MandatoryPrimaryLanOOBSupport", 1, 0, 8},
736        {"OptionalSecondaryLanOOBSupport", 2, 0, 8},
737        {"OptionalSerialOOBMTMODECapability", 3, 0, 8}}}}};
738 
739 ipmi_ret_t getDCMICapabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
740                                ipmi_request_t request, ipmi_response_t response,
741                                ipmi_data_len_t data_len, ipmi_context_t context)
742 {
743 
744     std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig);
745     if (!dcmiCapFile.is_open())
746     {
747         log<level::ERR>("DCMI Capabilities file not found");
748         return IPMI_CC_UNSPECIFIED_ERROR;
749     }
750 
751     auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
752     if (data.is_discarded())
753     {
754         log<level::ERR>("DCMI Capabilities JSON parser failure");
755         return IPMI_CC_UNSPECIFIED_ERROR;
756     }
757 
758     auto requestData =
759         reinterpret_cast<const dcmi::GetDCMICapRequest*>(request);
760 
761     // get list of capabilities in a parameter
762     auto caps =
763         dcmiCaps.find(static_cast<dcmi::DCMICapParameters>(requestData->param));
764     if (caps == dcmiCaps.end())
765     {
766         log<level::ERR>("Invalid input parameter");
767         return IPMI_CC_INVALID_FIELD_REQUEST;
768     }
769 
770     if (requestData->groupID != dcmi::groupExtId)
771     {
772         *data_len = 0;
773         return IPMI_CC_INVALID_FIELD_REQUEST;
774     }
775 
776     auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>(response);
777 
778     // For each capabilities in a parameter fill the data from
779     // the json file based on the capability name.
780     for (auto cap : caps->second.capList)
781     {
782         // If the data is beyond first byte boundary, insert in a
783         // 16bit pattern for example number of SEL entries are represented
784         // in 12bits.
785         if ((cap.length + cap.position) > dcmi::gByteBitSize)
786         {
787             uint16_t val = data.value(cap.name.c_str(), 0);
788             // According to DCMI spec v1.5, max number of SEL entries is
789             // 4096, but bit 12b of DCMI capabilities Mandatory Platform
790             // Attributes field is reserved and therefore we can use only
791             // the provided 12 bits with maximum value of 4095.
792             // We're playing safe here by applying the mask
793             // to ensure that provided value will fit into 12 bits.
794             if (cap.length > dcmi::gByteBitSize)
795             {
796                 val &= dcmi::gMaxSELEntriesMask;
797             }
798             val <<= cap.position;
799             responseData->data[cap.bytePosition - 1] |=
800                 static_cast<uint8_t>(val);
801             responseData->data[cap.bytePosition] |= val >> dcmi::gByteBitSize;
802         }
803         else
804         {
805             responseData->data[cap.bytePosition - 1] |=
806                 data.value(cap.name.c_str(), 0) << cap.position;
807         }
808     }
809 
810     responseData->groupID = dcmi::groupExtId;
811     responseData->major = DCMI_SPEC_MAJOR_VERSION;
812     responseData->minor = DCMI_SPEC_MINOR_VERSION;
813     responseData->paramRevision = DCMI_PARAMETER_REVISION;
814     *data_len = sizeof(*responseData) + caps->second.size;
815 
816     return IPMI_CC_OK;
817 }
818 
819 namespace dcmi
820 {
821 namespace temp_readings
822 {
823 
824 Temperature readTemp(const std::string& dbusService,
825                      const std::string& dbusPath)
826 {
827     // Read the temperature value from d-bus object. Need some conversion.
828     // As per the interface xyz.openbmc_project.Sensor.Value, the temperature
829     // is an double and in degrees C. It needs to be scaled by using the
830     // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t,
831     // with a separate single bit for the sign.
832 
833     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
834     auto result = ipmi::getAllDbusProperties(
835         bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value");
836     auto temperature =
837         std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value"));
838     double absTemp = std::abs(temperature);
839 
840     auto findFactor = result.find("Scale");
841     double factor = 0.0;
842     if (findFactor != result.end())
843     {
844         factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
845     }
846     double scale = std::pow(10, factor);
847 
848     auto tempDegrees = absTemp * scale;
849     // Max absolute temp as per ipmi spec is 128.
850     if (tempDegrees > maxTemp)
851     {
852         tempDegrees = maxTemp;
853     }
854 
855     return std::make_tuple(static_cast<uint8_t>(tempDegrees),
856                            (temperature < 0));
857 }
858 
859 std::tuple<Response, NumInstances> read(const std::string& type,
860                                         uint8_t instance)
861 {
862     Response response{};
863     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
864 
865     if (!instance)
866     {
867         log<level::ERR>("Expected non-zero instance");
868         elog<InternalFailure>();
869     }
870 
871     auto data = parseJSONConfig(gDCMISensorsConfig);
872     static const std::vector<Json> empty{};
873     std::vector<Json> readings = data.value(type, empty);
874     size_t numInstances = readings.size();
875     for (const auto& j : readings)
876     {
877         uint8_t instanceNum = j.value("instance", 0);
878         // Not the instance we're interested in
879         if (instanceNum != instance)
880         {
881             continue;
882         }
883 
884         std::string path = j.value("dbus", "");
885         std::string service;
886         try
887         {
888             service =
889                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
890         }
891         catch (std::exception& e)
892         {
893             log<level::DEBUG>(e.what());
894             return std::make_tuple(response, numInstances);
895         }
896 
897         response.instance = instance;
898         uint8_t temp{};
899         bool sign{};
900         std::tie(temp, sign) = readTemp(service, path);
901         response.temperature = temp;
902         response.sign = sign;
903 
904         // Found the instance we're interested in
905         break;
906     }
907 
908     if (numInstances > maxInstances)
909     {
910         numInstances = maxInstances;
911     }
912     return std::make_tuple(response, numInstances);
913 }
914 
915 std::tuple<ResponseList, NumInstances> readAll(const std::string& type,
916                                                uint8_t instanceStart)
917 {
918     ResponseList response{};
919     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
920 
921     size_t numInstances = 0;
922     auto data = parseJSONConfig(gDCMISensorsConfig);
923     static const std::vector<Json> empty{};
924     std::vector<Json> readings = data.value(type, empty);
925     numInstances = readings.size();
926     for (const auto& j : readings)
927     {
928         try
929         {
930             // Max of 8 response data sets
931             if (response.size() == maxDataSets)
932             {
933                 break;
934             }
935 
936             uint8_t instanceNum = j.value("instance", 0);
937             // Not in the instance range we're interested in
938             if (instanceNum < instanceStart)
939             {
940                 continue;
941             }
942 
943             std::string path = j.value("dbus", "");
944             auto service =
945                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
946 
947             Response r{};
948             r.instance = instanceNum;
949             uint8_t temp{};
950             bool sign{};
951             std::tie(temp, sign) = readTemp(service, path);
952             r.temperature = temp;
953             r.sign = sign;
954             response.push_back(r);
955         }
956         catch (std::exception& e)
957         {
958             log<level::DEBUG>(e.what());
959             continue;
960         }
961     }
962 
963     if (numInstances > maxInstances)
964     {
965         numInstances = maxInstances;
966     }
967     return std::make_tuple(response, numInstances);
968 }
969 
970 } // namespace temp_readings
971 } // namespace dcmi
972 
973 ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
974                            ipmi_request_t request, ipmi_response_t response,
975                            ipmi_data_len_t data_len, ipmi_context_t context)
976 {
977     auto requestData =
978         reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request);
979     auto responseData =
980         reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response);
981 
982     if (*data_len != sizeof(dcmi::GetTempReadingsRequest))
983     {
984         log<level::ERR>("Malformed request data",
985                         entry("DATA_SIZE=%d", *data_len));
986         return IPMI_CC_REQ_DATA_LEN_INVALID;
987     }
988     *data_len = 0;
989 
990     auto it = dcmi::entityIdToName.find(requestData->entityId);
991     if (it == dcmi::entityIdToName.end())
992     {
993         log<level::ERR>("Unknown Entity ID",
994                         entry("ENTITY_ID=%d", requestData->entityId));
995         return IPMI_CC_INVALID_FIELD_REQUEST;
996     }
997 
998     if (requestData->groupID != dcmi::groupExtId)
999     {
1000         log<level::ERR>("Invalid Group ID",
1001                         entry("GROUP_ID=%d", requestData->groupID));
1002         return IPMI_CC_INVALID_FIELD_REQUEST;
1003     }
1004 
1005     if (requestData->sensorType != dcmi::temperatureSensorType)
1006     {
1007         log<level::ERR>("Invalid sensor type",
1008                         entry("SENSOR_TYPE=%d", requestData->sensorType));
1009         return IPMI_CC_INVALID_FIELD_REQUEST;
1010     }
1011 
1012     dcmi::temp_readings::ResponseList temps{};
1013     try
1014     {
1015         if (!requestData->entityInstance)
1016         {
1017             // Read all instances
1018             std::tie(temps, responseData->numInstances) =
1019                 dcmi::temp_readings::readAll(it->second,
1020                                              requestData->instanceStart);
1021         }
1022         else
1023         {
1024             // Read one instance
1025             temps.resize(1);
1026             std::tie(temps[0], responseData->numInstances) =
1027                 dcmi::temp_readings::read(it->second,
1028                                           requestData->entityInstance);
1029         }
1030         responseData->numDataSets = temps.size();
1031     }
1032     catch (InternalFailure& e)
1033     {
1034         return IPMI_CC_UNSPECIFIED_ERROR;
1035     }
1036 
1037     responseData->groupID = dcmi::groupExtId;
1038     size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response);
1039     if (!temps.empty())
1040     {
1041         memcpy(responseData + 1, // copy payload right after the response header
1042                temps.data(), payloadSize);
1043     }
1044     *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize;
1045 
1046     return IPMI_CC_OK;
1047 }
1048 
1049 int64_t getPowerReading(sdbusplus::bus::bus& bus)
1050 {
1051     std::ifstream sensorFile(POWER_READING_SENSOR);
1052     std::string objectPath;
1053     if (!sensorFile.is_open())
1054     {
1055         log<level::ERR>("Power reading configuration file not found",
1056                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1057         elog<InternalFailure>();
1058     }
1059 
1060     auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1061     if (data.is_discarded())
1062     {
1063         log<level::ERR>("Error in parsing configuration file",
1064                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1065         elog<InternalFailure>();
1066     }
1067 
1068     objectPath = data.value("path", "");
1069     if (objectPath.empty())
1070     {
1071         log<level::ERR>("Power sensor D-Bus object path is empty",
1072                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1073         elog<InternalFailure>();
1074     }
1075 
1076     // Return default value if failed to read from D-Bus object
1077     int64_t power = 0;
1078     try
1079     {
1080         auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath);
1081 
1082         // Read the sensor value and scale properties
1083         auto properties = ipmi::getAllDbusProperties(bus, service, objectPath,
1084                                                      SENSOR_VALUE_INTF);
1085         auto value = std::get<int64_t>(properties[SENSOR_VALUE_PROP]);
1086         auto scale = std::get<int64_t>(properties[SENSOR_SCALE_PROP]);
1087 
1088         // Power reading needs to be scaled with the Scale value using the
1089         // formula Value * 10^Scale.
1090         power = value * std::pow(10, scale);
1091     }
1092     catch (std::exception& e)
1093     {
1094         log<level::INFO>("Failure to read power value from D-Bus object",
1095                          entry("OBJECT_PATH=%s", objectPath.c_str()),
1096                          entry("INTERFACE=%s", SENSOR_VALUE_INTF));
1097     }
1098     return power;
1099 }
1100 
1101 ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1102                              ipmi_request_t request, ipmi_response_t response,
1103                              ipmi_data_len_t data_len, ipmi_context_t context)
1104 {
1105     auto requestData =
1106         reinterpret_cast<const dcmi::SetConfParamsRequest*>(request);
1107     auto responseData =
1108         reinterpret_cast<dcmi::SetConfParamsResponse*>(response);
1109 
1110     if (requestData->groupID != dcmi::groupExtId ||
1111         *data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE ||
1112         *data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE)
1113     {
1114         log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
1115                         entry("GROUP_ID=%d", requestData->groupID),
1116                         entry("PACKET SIZE=%d", *data_len));
1117         return IPMI_CC_INVALID_FIELD_REQUEST;
1118     }
1119 
1120     *data_len = 0;
1121 
1122     try
1123     {
1124         // Take action based on the Parameter Selector
1125         switch (
1126             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1127         {
1128             case dcmi::DCMIConfigParameters::ActivateDHCP:
1129 
1130                 if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) &&
1131                     dcmi::getDHCPEnabled())
1132                 {
1133                     // When these conditions are met we have to trigger DHCP
1134                     // protocol restart using the latest parameter settings, but
1135                     // as per n/w manager design, each time when we update n/w
1136                     // parameters, n/w service is restarted. So we no need to
1137                     // take any action in this case.
1138                 }
1139                 break;
1140 
1141             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1142 
1143                 if (requestData->data[0] & DCMI_OPTION_12_MASK)
1144                 {
1145                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true);
1146                 }
1147                 else
1148                 {
1149                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false);
1150                 }
1151 
1152                 // Systemd-networkd doesn't support Random Back off
1153                 if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK)
1154                 {
1155                     return IPMI_CC_INVALID;
1156                 }
1157                 break;
1158             // Systemd-networkd doesn't allow to configure DHCP timigs
1159             case dcmi::DCMIConfigParameters::DHCPTiming1:
1160             case dcmi::DCMIConfigParameters::DHCPTiming2:
1161             case dcmi::DCMIConfigParameters::DHCPTiming3:
1162             default:
1163                 return IPMI_CC_INVALID;
1164         }
1165     }
1166     catch (std::exception& e)
1167     {
1168         log<level::ERR>(e.what());
1169         return IPMI_CC_UNSPECIFIED_ERROR;
1170     }
1171     responseData->groupID = dcmi::groupExtId;
1172     *data_len = sizeof(dcmi::SetConfParamsResponse);
1173 
1174     return IPMI_CC_OK;
1175 }
1176 
1177 ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1178                              ipmi_request_t request, ipmi_response_t response,
1179                              ipmi_data_len_t data_len, ipmi_context_t context)
1180 {
1181 
1182     auto requestData =
1183         reinterpret_cast<const dcmi::GetConfParamsRequest*>(request);
1184     auto responseData =
1185         reinterpret_cast<dcmi::GetConfParamsResponse*>(response);
1186 
1187     responseData->data[0] = 0x00;
1188 
1189     if (requestData->groupID != dcmi::groupExtId ||
1190         *data_len != sizeof(dcmi::GetConfParamsRequest))
1191     {
1192         log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
1193                         entry("GROUP_ID=%d", requestData->groupID),
1194                         entry("PACKET SIZE=%d", *data_len));
1195         return IPMI_CC_INVALID_FIELD_REQUEST;
1196     }
1197 
1198     *data_len = 0;
1199 
1200     try
1201     {
1202         // Take action based on the Parameter Selector
1203         switch (
1204             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1205         {
1206             case dcmi::DCMIConfigParameters::ActivateDHCP:
1207                 responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY;
1208                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1209                 break;
1210             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1211                 if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED))
1212                 {
1213                     responseData->data[0] |= DCMI_OPTION_12_MASK;
1214                 }
1215                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1216                 break;
1217             // Get below values from Systemd-networkd source code
1218             case dcmi::DCMIConfigParameters::DHCPTiming1:
1219                 responseData->data[0] = DHCP_TIMING1;
1220                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1221                 break;
1222             case dcmi::DCMIConfigParameters::DHCPTiming2:
1223                 responseData->data[0] = DHCP_TIMING2_LOWER;
1224                 responseData->data[1] = DHCP_TIMING2_UPPER;
1225                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1226                 break;
1227             case dcmi::DCMIConfigParameters::DHCPTiming3:
1228                 responseData->data[0] = DHCP_TIMING3_LOWER;
1229                 responseData->data[1] = DHCP_TIMING3_UPPER;
1230                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1231                 break;
1232             default:
1233                 *data_len = 0;
1234                 return IPMI_CC_INVALID;
1235         }
1236     }
1237     catch (std::exception& e)
1238     {
1239         log<level::ERR>(e.what());
1240         return IPMI_CC_UNSPECIFIED_ERROR;
1241     }
1242 
1243     responseData->groupID = dcmi::groupExtId;
1244     responseData->major = DCMI_SPEC_MAJOR_VERSION;
1245     responseData->minor = DCMI_SPEC_MINOR_VERSION;
1246     responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION;
1247 
1248     return IPMI_CC_OK;
1249 }
1250 
1251 ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1252                            ipmi_request_t request, ipmi_response_t response,
1253                            ipmi_data_len_t data_len, ipmi_context_t context)
1254 {
1255     if (!dcmi::isDCMIPowerMgmtSupported())
1256     {
1257         *data_len = 0;
1258         log<level::ERR>("DCMI Power management is unsupported!");
1259         return IPMI_CC_INVALID;
1260     }
1261 
1262     ipmi_ret_t rc = IPMI_CC_OK;
1263     auto requestData =
1264         reinterpret_cast<const dcmi::GetPowerReadingRequest*>(request);
1265     auto responseData =
1266         reinterpret_cast<dcmi::GetPowerReadingResponse*>(response);
1267 
1268     if (requestData->groupID != dcmi::groupExtId)
1269     {
1270         *data_len = 0;
1271         return IPMI_CC_INVALID_FIELD_REQUEST;
1272     }
1273 
1274     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
1275     int64_t power = 0;
1276     try
1277     {
1278         power = getPowerReading(bus);
1279     }
1280     catch (InternalFailure& e)
1281     {
1282         log<level::ERR>("Error in reading power sensor value",
1283                         entry("INTERFACE=%s", SENSOR_VALUE_INTF),
1284                         entry("PROPERTY=%s", SENSOR_VALUE_PROP));
1285         return IPMI_CC_UNSPECIFIED_ERROR;
1286     }
1287     responseData->groupID = dcmi::groupExtId;
1288 
1289     // TODO: openbmc/openbmc#2819
1290     // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
1291     // PowerReadingState readings need to be populated
1292     // after Telemetry changes.
1293     uint16_t totalPower = static_cast<uint16_t>(power);
1294     responseData->currentPower = totalPower;
1295     responseData->minimumPower = totalPower;
1296     responseData->maximumPower = totalPower;
1297     responseData->averagePower = totalPower;
1298 
1299     *data_len = sizeof(*responseData);
1300     return rc;
1301 }
1302 
1303 namespace dcmi
1304 {
1305 namespace sensor_info
1306 {
1307 
1308 Response createFromJson(const Json& config)
1309 {
1310     Response response{};
1311     uint16_t recordId = config.value("record_id", 0);
1312     response.recordIdLsb = recordId & 0xFF;
1313     response.recordIdMsb = (recordId >> 8) & 0xFF;
1314     return response;
1315 }
1316 
1317 std::tuple<Response, NumInstances> read(const std::string& type,
1318                                         uint8_t instance, const Json& config)
1319 {
1320     Response response{};
1321 
1322     if (!instance)
1323     {
1324         log<level::ERR>("Expected non-zero instance");
1325         elog<InternalFailure>();
1326     }
1327 
1328     static const std::vector<Json> empty{};
1329     std::vector<Json> readings = config.value(type, empty);
1330     size_t numInstances = readings.size();
1331     for (const auto& reading : readings)
1332     {
1333         uint8_t instanceNum = reading.value("instance", 0);
1334         // Not the instance we're interested in
1335         if (instanceNum != instance)
1336         {
1337             continue;
1338         }
1339 
1340         response = createFromJson(reading);
1341 
1342         // Found the instance we're interested in
1343         break;
1344     }
1345 
1346     if (numInstances > maxInstances)
1347     {
1348         log<level::DEBUG>("Trimming IPMI num instances",
1349                           entry("NUM_INSTANCES=%d", numInstances));
1350         numInstances = maxInstances;
1351     }
1352     return std::make_tuple(response, numInstances);
1353 }
1354 
1355 std::tuple<ResponseList, NumInstances>
1356     readAll(const std::string& type, uint8_t instanceStart, const Json& config)
1357 {
1358     ResponseList responses{};
1359 
1360     size_t numInstances = 0;
1361     static const std::vector<Json> empty{};
1362     std::vector<Json> readings = config.value(type, empty);
1363     numInstances = readings.size();
1364     for (const auto& reading : readings)
1365     {
1366         try
1367         {
1368             // Max of 8 records
1369             if (responses.size() == maxRecords)
1370             {
1371                 break;
1372             }
1373 
1374             uint8_t instanceNum = reading.value("instance", 0);
1375             // Not in the instance range we're interested in
1376             if (instanceNum < instanceStart)
1377             {
1378                 continue;
1379             }
1380 
1381             Response response = createFromJson(reading);
1382             responses.push_back(response);
1383         }
1384         catch (std::exception& e)
1385         {
1386             log<level::DEBUG>(e.what());
1387             continue;
1388         }
1389     }
1390 
1391     if (numInstances > maxInstances)
1392     {
1393         log<level::DEBUG>("Trimming IPMI num instances",
1394                           entry("NUM_INSTANCES=%d", numInstances));
1395         numInstances = maxInstances;
1396     }
1397     return std::make_tuple(responses, numInstances);
1398 }
1399 
1400 } // namespace sensor_info
1401 } // namespace dcmi
1402 
1403 ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1404                          ipmi_request_t request, ipmi_response_t response,
1405                          ipmi_data_len_t data_len, ipmi_context_t context)
1406 {
1407     auto requestData =
1408         reinterpret_cast<const dcmi::GetSensorInfoRequest*>(request);
1409     auto responseData =
1410         reinterpret_cast<dcmi::GetSensorInfoResponseHdr*>(response);
1411 
1412     if (*data_len != sizeof(dcmi::GetSensorInfoRequest))
1413     {
1414         log<level::ERR>("Malformed request data",
1415                         entry("DATA_SIZE=%d", *data_len));
1416         return IPMI_CC_REQ_DATA_LEN_INVALID;
1417     }
1418     *data_len = 0;
1419 
1420     auto it = dcmi::entityIdToName.find(requestData->entityId);
1421     if (it == dcmi::entityIdToName.end())
1422     {
1423         log<level::ERR>("Unknown Entity ID",
1424                         entry("ENTITY_ID=%d", requestData->entityId));
1425         return IPMI_CC_INVALID_FIELD_REQUEST;
1426     }
1427 
1428     if (requestData->groupID != dcmi::groupExtId)
1429     {
1430         log<level::ERR>("Invalid Group ID",
1431                         entry("GROUP_ID=%d", requestData->groupID));
1432         return IPMI_CC_INVALID_FIELD_REQUEST;
1433     }
1434 
1435     if (requestData->sensorType != dcmi::temperatureSensorType)
1436     {
1437         log<level::ERR>("Invalid sensor type",
1438                         entry("SENSOR_TYPE=%d", requestData->sensorType));
1439         return IPMI_CC_INVALID_FIELD_REQUEST;
1440     }
1441 
1442     dcmi::sensor_info::ResponseList sensors{};
1443     static dcmi::Json config{};
1444     static bool parsed = false;
1445 
1446     try
1447     {
1448         if (!parsed)
1449         {
1450             config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
1451             parsed = true;
1452         }
1453 
1454         if (!requestData->entityInstance)
1455         {
1456             // Read all instances
1457             std::tie(sensors, responseData->numInstances) =
1458                 dcmi::sensor_info::readAll(it->second,
1459                                            requestData->instanceStart, config);
1460         }
1461         else
1462         {
1463             // Read one instance
1464             sensors.resize(1);
1465             std::tie(sensors[0], responseData->numInstances) =
1466                 dcmi::sensor_info::read(it->second, requestData->entityInstance,
1467                                         config);
1468         }
1469         responseData->numRecords = sensors.size();
1470     }
1471     catch (InternalFailure& e)
1472     {
1473         return IPMI_CC_UNSPECIFIED_ERROR;
1474     }
1475 
1476     responseData->groupID = dcmi::groupExtId;
1477     size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response);
1478     if (!sensors.empty())
1479     {
1480         memcpy(responseData + 1, // copy payload right after the response header
1481                sensors.data(), payloadSize);
1482     }
1483     *data_len = sizeof(dcmi::GetSensorInfoResponseHdr) + payloadSize;
1484 
1485     return IPMI_CC_OK;
1486 }
1487 
1488 void register_netfn_dcmi_functions()
1489 {
1490     // <Get Power Limit>
1491 
1492     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL,
1493                            getPowerLimit, PRIVILEGE_USER);
1494 
1495     // <Set Power Limit>
1496 
1497     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL,
1498                            setPowerLimit, PRIVILEGE_OPERATOR);
1499 
1500     // <Activate/Deactivate Power Limit>
1501 
1502     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT,
1503                            NULL, applyPowerLimit, PRIVILEGE_OPERATOR);
1504 
1505     // <Get Asset Tag>
1506 
1507     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL,
1508                            getAssetTag, PRIVILEGE_USER);
1509 
1510     // <Set Asset Tag>
1511 
1512     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL,
1513                            setAssetTag, PRIVILEGE_OPERATOR);
1514 
1515     // <Get Management Controller Identifier String>
1516 
1517     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR,
1518                            NULL, getMgmntCtrlIdStr, PRIVILEGE_USER);
1519 
1520     // <Set Management Controller Identifier String>
1521     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR,
1522                            NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN);
1523 
1524     // <Get DCMI capabilities>
1525     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES,
1526                            NULL, getDCMICapabilities, PRIVILEGE_USER);
1527 
1528     // <Get Temperature Readings>
1529     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS,
1530                            NULL, getTempReadings, PRIVILEGE_USER);
1531 
1532     // <Get Power Reading>
1533     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING,
1534                            NULL, getPowerReading, PRIVILEGE_USER);
1535 
1536     // <Get Sensor Info>
1537     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO, NULL,
1538                            getSensorInfo, PRIVILEGE_USER);
1539 
1540     // <Get DCMI Configuration Parameters>
1541     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS, NULL,
1542                            getDCMIConfParams, PRIVILEGE_USER);
1543 
1544     // <Set DCMI Configuration Parameters>
1545     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS, NULL,
1546                            setDCMIConfParams, PRIVILEGE_ADMIN);
1547 
1548     return;
1549 }
1550 // 956379
1551