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 = 0x04;
41 constexpr auto DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE = 0x03;
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     std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
314     auto responseData =
315         reinterpret_cast<dcmi::GetPowerLimitResponse*>(outPayload.data());
316 
317     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
318     uint32_t pcapValue = 0;
319     bool pcapEnable = false;
320 
321     try
322     {
323         pcapValue = dcmi::getPcap(sdbus);
324         pcapEnable = dcmi::getPcapEnabled(sdbus);
325     }
326     catch (InternalFailure& e)
327     {
328         *data_len = 0;
329         return IPMI_CC_UNSPECIFIED_ERROR;
330     }
331 
332     /*
333      * Exception action if power limit is exceeded and cannot be controlled
334      * with the correction time limit is hardcoded to Hard Power Off system
335      * and log event to SEL.
336      */
337     constexpr auto exception = 0x01;
338     responseData->exceptionAction = exception;
339 
340     responseData->powerLimit = static_cast<uint16_t>(pcapValue);
341 
342     /*
343      * Correction time limit and Statistics sampling period is currently not
344      * populated.
345      */
346 
347     *data_len = outPayload.size();
348     memcpy(response, outPayload.data(), *data_len);
349 
350     if (pcapEnable)
351     {
352         return IPMI_CC_OK;
353     }
354     else
355     {
356         return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT;
357     }
358 }
359 
360 ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
361                          ipmi_request_t request, ipmi_response_t response,
362                          ipmi_data_len_t data_len, ipmi_context_t context)
363 {
364     if (!dcmi::isDCMIPowerMgmtSupported())
365     {
366         *data_len = 0;
367         log<level::ERR>("DCMI Power management is unsupported!");
368         return IPMI_CC_INVALID;
369     }
370 
371     auto requestData =
372         reinterpret_cast<const dcmi::SetPowerLimitRequest*>(request);
373 
374     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
375 
376     // Only process the power limit requested in watts.
377     try
378     {
379         dcmi::setPcap(sdbus, requestData->powerLimit);
380     }
381     catch (InternalFailure& e)
382     {
383         *data_len = 0;
384         return IPMI_CC_UNSPECIFIED_ERROR;
385     }
386 
387     log<level::INFO>("Set Power Cap",
388                      entry("POWERCAP=%u", requestData->powerLimit));
389 
390     *data_len = 0;
391     return IPMI_CC_OK;
392 }
393 
394 ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
395                            ipmi_request_t request, ipmi_response_t response,
396                            ipmi_data_len_t data_len, ipmi_context_t context)
397 {
398     if (!dcmi::isDCMIPowerMgmtSupported())
399     {
400         *data_len = 0;
401         log<level::ERR>("DCMI Power management is unsupported!");
402         return IPMI_CC_INVALID;
403     }
404 
405     auto requestData =
406         reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>(request);
407 
408     sdbusplus::bus::bus sdbus{ipmid_get_sd_bus_connection()};
409 
410     try
411     {
412         dcmi::setPcapEnable(sdbus,
413                             static_cast<bool>(requestData->powerLimitAction));
414     }
415     catch (InternalFailure& e)
416     {
417         *data_len = 0;
418         return IPMI_CC_UNSPECIFIED_ERROR;
419     }
420 
421     log<level::INFO>("Set Power Cap Enable",
422                      entry("POWERCAPENABLE=%u", requestData->powerLimitAction));
423 
424     *data_len = 0;
425     return IPMI_CC_OK;
426 }
427 
428 ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
429                        ipmi_request_t request, ipmi_response_t response,
430                        ipmi_data_len_t data_len, ipmi_context_t context)
431 {
432     auto requestData =
433         reinterpret_cast<const dcmi::GetAssetTagRequest*>(request);
434     std::vector<uint8_t> outPayload(sizeof(dcmi::GetAssetTagResponse));
435     auto responseData =
436         reinterpret_cast<dcmi::GetAssetTagResponse*>(outPayload.data());
437 
438     // Verify offset to read and number of bytes to read are not exceeding the
439     // range.
440     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
441         (requestData->bytes > dcmi::maxBytes) ||
442         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
443     {
444         *data_len = 0;
445         return IPMI_CC_PARM_OUT_OF_RANGE;
446     }
447 
448     std::string assetTag;
449 
450     try
451     {
452         assetTag = dcmi::readAssetTag();
453     }
454     catch (InternalFailure& e)
455     {
456         *data_len = 0;
457         return IPMI_CC_UNSPECIFIED_ERROR;
458     }
459 
460     // Return if the asset tag is not populated.
461     if (!assetTag.size())
462     {
463         responseData->tagLength = 0;
464         memcpy(response, outPayload.data(), outPayload.size());
465         *data_len = outPayload.size();
466         return IPMI_CC_OK;
467     }
468 
469     // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit
470     // Get Asset Tag command.
471     if (assetTag.size() > dcmi::assetTagMaxSize)
472     {
473         assetTag.resize(dcmi::assetTagMaxSize);
474     }
475 
476     // If the requested offset is beyond the asset tag size.
477     if (requestData->offset >= assetTag.size())
478     {
479         *data_len = 0;
480         return IPMI_CC_PARM_OUT_OF_RANGE;
481     }
482 
483     auto returnData = assetTag.substr(requestData->offset, requestData->bytes);
484 
485     responseData->tagLength = assetTag.size();
486 
487     memcpy(response, outPayload.data(), outPayload.size());
488     memcpy(static_cast<uint8_t*>(response) + outPayload.size(),
489            returnData.data(), returnData.size());
490     *data_len = outPayload.size() + returnData.size();
491 
492     return IPMI_CC_OK;
493 }
494 
495 ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
496                        ipmi_request_t request, ipmi_response_t response,
497                        ipmi_data_len_t data_len, ipmi_context_t context)
498 {
499     auto requestData =
500         reinterpret_cast<const dcmi::SetAssetTagRequest*>(request);
501     std::vector<uint8_t> outPayload(sizeof(dcmi::SetAssetTagResponse));
502     auto responseData =
503         reinterpret_cast<dcmi::SetAssetTagResponse*>(outPayload.data());
504 
505     // Verify offset to read and number of bytes to read are not exceeding the
506     // range.
507     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
508         (requestData->bytes > dcmi::maxBytes) ||
509         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
510     {
511         *data_len = 0;
512         return IPMI_CC_PARM_OUT_OF_RANGE;
513     }
514 
515     std::string assetTag;
516 
517     try
518     {
519         assetTag = dcmi::readAssetTag();
520 
521         if (requestData->offset > assetTag.size())
522         {
523             *data_len = 0;
524             return IPMI_CC_PARM_OUT_OF_RANGE;
525         }
526 
527         assetTag.replace(requestData->offset,
528                          assetTag.size() - requestData->offset,
529                          static_cast<const char*>(request) +
530                              sizeof(dcmi::SetAssetTagRequest),
531                          requestData->bytes);
532 
533         dcmi::writeAssetTag(assetTag);
534 
535         responseData->tagLength = assetTag.size();
536         memcpy(response, outPayload.data(), outPayload.size());
537         *data_len = outPayload.size();
538 
539         return IPMI_CC_OK;
540     }
541     catch (InternalFailure& e)
542     {
543         *data_len = 0;
544         return IPMI_CC_UNSPECIFIED_ERROR;
545     }
546 }
547 
548 ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
549                              ipmi_request_t request, ipmi_response_t response,
550                              ipmi_data_len_t data_len, ipmi_context_t context)
551 {
552     auto requestData =
553         reinterpret_cast<const dcmi::GetMgmntCtrlIdStrRequest*>(request);
554     auto responseData =
555         reinterpret_cast<dcmi::GetMgmntCtrlIdStrResponse*>(response);
556     std::string hostName;
557 
558     *data_len = 0;
559 
560     if (requestData->bytes > dcmi::maxBytes ||
561         requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen)
562     {
563         return IPMI_CC_INVALID_FIELD_REQUEST;
564     }
565 
566     try
567     {
568         hostName = dcmi::getHostName();
569     }
570     catch (InternalFailure& e)
571     {
572         return IPMI_CC_UNSPECIFIED_ERROR;
573     }
574 
575     if (requestData->offset > hostName.length())
576     {
577         return IPMI_CC_PARM_OUT_OF_RANGE;
578     }
579     auto responseStr = hostName.substr(requestData->offset, requestData->bytes);
580     auto responseStrLen = std::min(static_cast<std::size_t>(requestData->bytes),
581                                    responseStr.length() + 1);
582     responseData->strLen = hostName.length();
583     std::copy(begin(responseStr), end(responseStr), responseData->data);
584 
585     *data_len = sizeof(*responseData) + responseStrLen;
586     return IPMI_CC_OK;
587 }
588 
589 ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
590                              ipmi_request_t request, ipmi_response_t response,
591                              ipmi_data_len_t data_len, ipmi_context_t context)
592 {
593     static std::array<char, dcmi::maxCtrlIdStrLen + 1> newCtrlIdStr;
594 
595     auto requestData =
596         reinterpret_cast<const dcmi::SetMgmntCtrlIdStrRequest*>(request);
597     auto responseData =
598         reinterpret_cast<dcmi::SetMgmntCtrlIdStrResponse*>(response);
599 
600     *data_len = 0;
601 
602     if (requestData->bytes > dcmi::maxBytes ||
603         requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 ||
604         (requestData->offset + requestData->bytes ==
605              dcmi::maxCtrlIdStrLen + 1 &&
606          requestData->data[requestData->bytes - 1] != '\0'))
607     {
608         return IPMI_CC_INVALID_FIELD_REQUEST;
609     }
610 
611     try
612     {
613         /* if there is no old value and offset is not 0 */
614         if (newCtrlIdStr[0] == '\0' && requestData->offset != 0)
615         {
616             /* read old ctrlIdStr */
617             auto hostName = dcmi::getHostName();
618             hostName.resize(dcmi::maxCtrlIdStrLen);
619             std::copy(begin(hostName), end(hostName), begin(newCtrlIdStr));
620             newCtrlIdStr[hostName.length()] = '\0';
621         }
622 
623         /* replace part of string and mark byte after the last as \0 */
624         auto restStrIter =
625             std::copy_n(requestData->data, requestData->bytes,
626                         begin(newCtrlIdStr) + requestData->offset);
627         /* if the last written byte is not 64th - add '\0' */
628         if (requestData->offset + requestData->bytes <= dcmi::maxCtrlIdStrLen)
629         {
630             *restStrIter = '\0';
631         }
632 
633         /* if input data contains '\0' whole string is sent - update hostname */
634         auto it = std::find(requestData->data,
635                             requestData->data + requestData->bytes, '\0');
636         if (it != requestData->data + requestData->bytes)
637         {
638             sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
639             ipmi::setDbusProperty(bus, dcmi::networkServiceName,
640                                   dcmi::networkConfigObj,
641                                   dcmi::networkConfigIntf, dcmi::hostNameProp,
642                                   std::string(newCtrlIdStr.data()));
643         }
644     }
645     catch (InternalFailure& e)
646     {
647         *data_len = 0;
648         return IPMI_CC_UNSPECIFIED_ERROR;
649     }
650 
651     responseData->offset = requestData->offset + requestData->bytes;
652     *data_len = sizeof(*responseData);
653     return IPMI_CC_OK;
654 }
655 
656 // List of the capabilities under each parameter
657 dcmi::DCMICaps dcmiCaps = {
658     // Supported DCMI Capabilities
659     {dcmi::DCMICapParameters::SUPPORTED_DCMI_CAPS,
660      {3,
661       {{"PowerManagement", 2, 0, 1},
662        {"OOBSecondaryLan", 3, 2, 1},
663        {"SerialTMODE", 3, 1, 1},
664        {"InBandSystemInterfaceChannel", 3, 0, 1}}}},
665     // Mandatory Platform Attributes
666     {dcmi::DCMICapParameters::MANDATORY_PLAT_ATTRIBUTES,
667      {5,
668       {{"SELAutoRollOver", 1, 15, 1},
669        {"FlushEntireSELUponRollOver", 1, 14, 1},
670        {"RecordLevelSELFlushUponRollOver", 1, 13, 1},
671        {"NumberOfSELEntries", 1, 0, 12},
672        {"TempMonitoringSamplingFreq", 5, 0, 8}}}},
673     // Optional Platform Attributes
674     {dcmi::DCMICapParameters::OPTIONAL_PLAT_ATTRIBUTES,
675      {2,
676       {{"PowerMgmtDeviceSlaveAddress", 1, 1, 7},
677        {"BMCChannelNumber", 2, 4, 4},
678        {"DeviceRivision", 2, 0, 4}}}},
679     // Manageability Access Attributes
680     {dcmi::DCMICapParameters::MANAGEABILITY_ACCESS_ATTRIBUTES,
681      {3,
682       {{"MandatoryPrimaryLanOOBSupport", 1, 0, 8},
683        {"OptionalSecondaryLanOOBSupport", 2, 0, 8},
684        {"OptionalSerialOOBMTMODECapability", 3, 0, 8}}}}};
685 
686 ipmi_ret_t getDCMICapabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
687                                ipmi_request_t request, ipmi_response_t response,
688                                ipmi_data_len_t data_len, ipmi_context_t context)
689 {
690 
691     std::ifstream dcmiCapFile(dcmi::gDCMICapabilitiesConfig);
692     if (!dcmiCapFile.is_open())
693     {
694         log<level::ERR>("DCMI Capabilities file not found");
695         return IPMI_CC_UNSPECIFIED_ERROR;
696     }
697 
698     auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
699     if (data.is_discarded())
700     {
701         log<level::ERR>("DCMI Capabilities JSON parser failure");
702         return IPMI_CC_UNSPECIFIED_ERROR;
703     }
704 
705     auto requestData =
706         reinterpret_cast<const dcmi::GetDCMICapRequest*>(request);
707 
708     // get list of capabilities in a parameter
709     auto caps =
710         dcmiCaps.find(static_cast<dcmi::DCMICapParameters>(requestData->param));
711     if (caps == dcmiCaps.end())
712     {
713         log<level::ERR>("Invalid input parameter");
714         return IPMI_CC_INVALID_FIELD_REQUEST;
715     }
716 
717     auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>(response);
718 
719     // For each capabilities in a parameter fill the data from
720     // the json file based on the capability name.
721     for (auto cap : caps->second.capList)
722     {
723         // If the data is beyond first byte boundary, insert in a
724         // 16bit pattern for example number of SEL entries are represented
725         // in 12bits.
726         if ((cap.length + cap.position) > dcmi::gByteBitSize)
727         {
728             uint16_t val = data.value(cap.name.c_str(), 0);
729             // According to DCMI spec v1.5, max number of SEL entries is
730             // 4096, but bit 12b of DCMI capabilities Mandatory Platform
731             // Attributes field is reserved and therefore we can use only
732             // the provided 12 bits with maximum value of 4095.
733             // We're playing safe here by applying the mask
734             // to ensure that provided value will fit into 12 bits.
735             if (cap.length > dcmi::gByteBitSize)
736             {
737                 val &= dcmi::gMaxSELEntriesMask;
738             }
739             val <<= cap.position;
740             responseData->data[cap.bytePosition - 1] |=
741                 static_cast<uint8_t>(val);
742             responseData->data[cap.bytePosition] |= val >> dcmi::gByteBitSize;
743         }
744         else
745         {
746             responseData->data[cap.bytePosition - 1] |=
747                 data.value(cap.name.c_str(), 0) << cap.position;
748         }
749     }
750 
751     responseData->major = DCMI_SPEC_MAJOR_VERSION;
752     responseData->minor = DCMI_SPEC_MINOR_VERSION;
753     responseData->paramRevision = DCMI_PARAMETER_REVISION;
754     *data_len = sizeof(*responseData) + caps->second.size;
755 
756     return IPMI_CC_OK;
757 }
758 
759 namespace dcmi
760 {
761 namespace temp_readings
762 {
763 
764 Temperature readTemp(const std::string& dbusService,
765                      const std::string& dbusPath)
766 {
767     // Read the temperature value from d-bus object. Need some conversion.
768     // As per the interface xyz.openbmc_project.Sensor.Value, the temperature
769     // is an double and in degrees C. It needs to be scaled by using the
770     // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t,
771     // with a separate single bit for the sign.
772 
773     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
774     auto result = ipmi::getAllDbusProperties(
775         bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value");
776     auto temperature =
777         std::visit(ipmi::VariantToDoubleVisitor(), result.at("Value"));
778     double absTemp = std::abs(temperature);
779 
780     auto findFactor = result.find("Scale");
781     double factor = 0.0;
782     if (findFactor != result.end())
783     {
784         factor = std::visit(ipmi::VariantToDoubleVisitor(), findFactor->second);
785     }
786     double scale = std::pow(10, factor);
787 
788     auto tempDegrees = absTemp * scale;
789     // Max absolute temp as per ipmi spec is 128.
790     if (tempDegrees > maxTemp)
791     {
792         tempDegrees = maxTemp;
793     }
794 
795     return std::make_tuple(static_cast<uint8_t>(tempDegrees),
796                            (temperature < 0));
797 }
798 
799 std::tuple<Response, NumInstances> read(const std::string& type,
800                                         uint8_t instance)
801 {
802     Response response{};
803     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
804 
805     if (!instance)
806     {
807         log<level::ERR>("Expected non-zero instance");
808         elog<InternalFailure>();
809     }
810 
811     auto data = parseJSONConfig(gDCMISensorsConfig);
812     static const std::vector<Json> empty{};
813     std::vector<Json> readings = data.value(type, empty);
814     size_t numInstances = readings.size();
815     for (const auto& j : readings)
816     {
817         uint8_t instanceNum = j.value("instance", 0);
818         // Not the instance we're interested in
819         if (instanceNum != instance)
820         {
821             continue;
822         }
823 
824         std::string path = j.value("dbus", "");
825         std::string service;
826         try
827         {
828             service =
829                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
830         }
831         catch (std::exception& e)
832         {
833             log<level::DEBUG>(e.what());
834             return std::make_tuple(response, numInstances);
835         }
836 
837         response.instance = instance;
838         uint8_t temp{};
839         bool sign{};
840         std::tie(temp, sign) = readTemp(service, path);
841         response.temperature = temp;
842         response.sign = sign;
843 
844         // Found the instance we're interested in
845         break;
846     }
847 
848     if (numInstances > maxInstances)
849     {
850         numInstances = maxInstances;
851     }
852     return std::make_tuple(response, numInstances);
853 }
854 
855 std::tuple<ResponseList, NumInstances> readAll(const std::string& type,
856                                                uint8_t instanceStart)
857 {
858     ResponseList response{};
859     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
860 
861     size_t numInstances = 0;
862     auto data = parseJSONConfig(gDCMISensorsConfig);
863     static const std::vector<Json> empty{};
864     std::vector<Json> readings = data.value(type, empty);
865     numInstances = readings.size();
866     for (const auto& j : readings)
867     {
868         try
869         {
870             // Max of 8 response data sets
871             if (response.size() == maxDataSets)
872             {
873                 break;
874             }
875 
876             uint8_t instanceNum = j.value("instance", 0);
877             // Not in the instance range we're interested in
878             if (instanceNum < instanceStart)
879             {
880                 continue;
881             }
882 
883             std::string path = j.value("dbus", "");
884             auto service =
885                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
886 
887             Response r{};
888             r.instance = instanceNum;
889             uint8_t temp{};
890             bool sign{};
891             std::tie(temp, sign) = readTemp(service, path);
892             r.temperature = temp;
893             r.sign = sign;
894             response.push_back(r);
895         }
896         catch (std::exception& e)
897         {
898             log<level::DEBUG>(e.what());
899             continue;
900         }
901     }
902 
903     if (numInstances > maxInstances)
904     {
905         numInstances = maxInstances;
906     }
907     return std::make_tuple(response, numInstances);
908 }
909 
910 } // namespace temp_readings
911 } // namespace dcmi
912 
913 ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
914                            ipmi_request_t request, ipmi_response_t response,
915                            ipmi_data_len_t data_len, ipmi_context_t context)
916 {
917     auto requestData =
918         reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request);
919     auto responseData =
920         reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response);
921 
922     if (*data_len != sizeof(dcmi::GetTempReadingsRequest))
923     {
924         log<level::ERR>("Malformed request data",
925                         entry("DATA_SIZE=%d", *data_len));
926         return IPMI_CC_REQ_DATA_LEN_INVALID;
927     }
928     *data_len = 0;
929 
930     auto it = dcmi::entityIdToName.find(requestData->entityId);
931     if (it == dcmi::entityIdToName.end())
932     {
933         log<level::ERR>("Unknown Entity ID",
934                         entry("ENTITY_ID=%d", requestData->entityId));
935         return IPMI_CC_INVALID_FIELD_REQUEST;
936     }
937 
938     if (requestData->sensorType != dcmi::temperatureSensorType)
939     {
940         log<level::ERR>("Invalid sensor type",
941                         entry("SENSOR_TYPE=%d", requestData->sensorType));
942         return IPMI_CC_INVALID_FIELD_REQUEST;
943     }
944 
945     dcmi::temp_readings::ResponseList temps{};
946     try
947     {
948         if (!requestData->entityInstance)
949         {
950             // Read all instances
951             std::tie(temps, responseData->numInstances) =
952                 dcmi::temp_readings::readAll(it->second,
953                                              requestData->instanceStart);
954         }
955         else
956         {
957             // Read one instance
958             temps.resize(1);
959             std::tie(temps[0], responseData->numInstances) =
960                 dcmi::temp_readings::read(it->second,
961                                           requestData->entityInstance);
962         }
963         responseData->numDataSets = temps.size();
964     }
965     catch (InternalFailure& e)
966     {
967         return IPMI_CC_UNSPECIFIED_ERROR;
968     }
969 
970     size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response);
971     if (!temps.empty())
972     {
973         memcpy(responseData + 1, // copy payload right after the response header
974                temps.data(), payloadSize);
975     }
976     *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize;
977 
978     return IPMI_CC_OK;
979 }
980 
981 int64_t getPowerReading(sdbusplus::bus::bus& bus)
982 {
983     std::ifstream sensorFile(POWER_READING_SENSOR);
984     std::string objectPath;
985     if (!sensorFile.is_open())
986     {
987         log<level::ERR>("Power reading configuration file not found",
988                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
989         elog<InternalFailure>();
990     }
991 
992     auto data = nlohmann::json::parse(sensorFile, nullptr, false);
993     if (data.is_discarded())
994     {
995         log<level::ERR>("Error in parsing configuration file",
996                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
997         elog<InternalFailure>();
998     }
999 
1000     objectPath = data.value("path", "");
1001     if (objectPath.empty())
1002     {
1003         log<level::ERR>("Power sensor D-Bus object path is empty",
1004                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1005         elog<InternalFailure>();
1006     }
1007 
1008     // Return default value if failed to read from D-Bus object
1009     int64_t power = 0;
1010     try
1011     {
1012         auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath);
1013 
1014         // Read the sensor value and scale properties
1015         auto properties = ipmi::getAllDbusProperties(bus, service, objectPath,
1016                                                      SENSOR_VALUE_INTF);
1017         auto value = std::get<int64_t>(properties[SENSOR_VALUE_PROP]);
1018         auto scale = std::get<int64_t>(properties[SENSOR_SCALE_PROP]);
1019 
1020         // Power reading needs to be scaled with the Scale value using the
1021         // formula Value * 10^Scale.
1022         power = value * std::pow(10, scale);
1023     }
1024     catch (std::exception& e)
1025     {
1026         log<level::INFO>("Failure to read power value from D-Bus object",
1027                          entry("OBJECT_PATH=%s", objectPath.c_str()),
1028                          entry("INTERFACE=%s", SENSOR_VALUE_INTF));
1029     }
1030     return power;
1031 }
1032 
1033 ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1034                              ipmi_request_t request, ipmi_response_t response,
1035                              ipmi_data_len_t data_len, ipmi_context_t context)
1036 {
1037     auto requestData =
1038         reinterpret_cast<const dcmi::SetConfParamsRequest*>(request);
1039 
1040     if (*data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE ||
1041         *data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE)
1042     {
1043         log<level::ERR>("Invalid Requested Packet size",
1044                         entry("PACKET SIZE=%d", *data_len));
1045         *data_len = 0;
1046         return IPMI_CC_INVALID_FIELD_REQUEST;
1047     }
1048     *data_len = 0;
1049 
1050     try
1051     {
1052         // Take action based on the Parameter Selector
1053         switch (
1054             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1055         {
1056             case dcmi::DCMIConfigParameters::ActivateDHCP:
1057 
1058                 if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) &&
1059                     dcmi::getDHCPEnabled())
1060                 {
1061                     // When these conditions are met we have to trigger DHCP
1062                     // protocol restart using the latest parameter settings, but
1063                     // as per n/w manager design, each time when we update n/w
1064                     // parameters, n/w service is restarted. So we no need to
1065                     // take any action in this case.
1066                 }
1067                 break;
1068 
1069             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1070 
1071                 if (requestData->data[0] & DCMI_OPTION_12_MASK)
1072                 {
1073                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true);
1074                 }
1075                 else
1076                 {
1077                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false);
1078                 }
1079 
1080                 // Systemd-networkd doesn't support Random Back off
1081                 if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK)
1082                 {
1083                     return IPMI_CC_INVALID;
1084                 }
1085                 break;
1086             // Systemd-networkd doesn't allow to configure DHCP timigs
1087             case dcmi::DCMIConfigParameters::DHCPTiming1:
1088             case dcmi::DCMIConfigParameters::DHCPTiming2:
1089             case dcmi::DCMIConfigParameters::DHCPTiming3:
1090             default:
1091                 return IPMI_CC_INVALID;
1092         }
1093     }
1094     catch (std::exception& e)
1095     {
1096         log<level::ERR>(e.what());
1097         return IPMI_CC_UNSPECIFIED_ERROR;
1098     }
1099     return IPMI_CC_OK;
1100 }
1101 
1102 ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1103                              ipmi_request_t request, ipmi_response_t response,
1104                              ipmi_data_len_t data_len, ipmi_context_t context)
1105 {
1106 
1107     auto requestData =
1108         reinterpret_cast<const dcmi::GetConfParamsRequest*>(request);
1109     auto responseData =
1110         reinterpret_cast<dcmi::GetConfParamsResponse*>(response);
1111 
1112     responseData->data[0] = 0x00;
1113 
1114     if (*data_len != sizeof(dcmi::GetConfParamsRequest))
1115     {
1116         log<level::ERR>("Invalid Requested Packet size",
1117                         entry("PACKET SIZE=%d", *data_len));
1118         return IPMI_CC_INVALID_FIELD_REQUEST;
1119     }
1120 
1121     *data_len = 0;
1122 
1123     try
1124     {
1125         // Take action based on the Parameter Selector
1126         switch (
1127             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1128         {
1129             case dcmi::DCMIConfigParameters::ActivateDHCP:
1130                 responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY;
1131                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1132                 break;
1133             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1134                 if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED))
1135                 {
1136                     responseData->data[0] |= DCMI_OPTION_12_MASK;
1137                 }
1138                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1139                 break;
1140             // Get below values from Systemd-networkd source code
1141             case dcmi::DCMIConfigParameters::DHCPTiming1:
1142                 responseData->data[0] = DHCP_TIMING1;
1143                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1144                 break;
1145             case dcmi::DCMIConfigParameters::DHCPTiming2:
1146                 responseData->data[0] = DHCP_TIMING2_LOWER;
1147                 responseData->data[1] = DHCP_TIMING2_UPPER;
1148                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1149                 break;
1150             case dcmi::DCMIConfigParameters::DHCPTiming3:
1151                 responseData->data[0] = DHCP_TIMING3_LOWER;
1152                 responseData->data[1] = DHCP_TIMING3_UPPER;
1153                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1154                 break;
1155             default:
1156                 *data_len = 0;
1157                 return IPMI_CC_INVALID;
1158         }
1159     }
1160     catch (std::exception& e)
1161     {
1162         log<level::ERR>(e.what());
1163         return IPMI_CC_UNSPECIFIED_ERROR;
1164     }
1165 
1166     responseData->major = DCMI_SPEC_MAJOR_VERSION;
1167     responseData->minor = DCMI_SPEC_MINOR_VERSION;
1168     responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION;
1169 
1170     return IPMI_CC_OK;
1171 }
1172 
1173 ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1174                            ipmi_request_t request, ipmi_response_t response,
1175                            ipmi_data_len_t data_len, ipmi_context_t context)
1176 {
1177     if (!dcmi::isDCMIPowerMgmtSupported())
1178     {
1179         *data_len = 0;
1180         log<level::ERR>("DCMI Power management is unsupported!");
1181         return IPMI_CC_INVALID;
1182     }
1183 
1184     ipmi_ret_t rc = IPMI_CC_OK;
1185     auto responseData =
1186         reinterpret_cast<dcmi::GetPowerReadingResponse*>(response);
1187 
1188     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
1189     int64_t power = 0;
1190     try
1191     {
1192         power = getPowerReading(bus);
1193     }
1194     catch (InternalFailure& e)
1195     {
1196         log<level::ERR>("Error in reading power sensor value",
1197                         entry("INTERFACE=%s", SENSOR_VALUE_INTF),
1198                         entry("PROPERTY=%s", SENSOR_VALUE_PROP));
1199         return IPMI_CC_UNSPECIFIED_ERROR;
1200     }
1201 
1202     // TODO: openbmc/openbmc#2819
1203     // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
1204     // PowerReadingState readings need to be populated
1205     // after Telemetry changes.
1206     uint16_t totalPower = static_cast<uint16_t>(power);
1207     responseData->currentPower = totalPower;
1208     responseData->minimumPower = totalPower;
1209     responseData->maximumPower = totalPower;
1210     responseData->averagePower = totalPower;
1211 
1212     *data_len = sizeof(*responseData);
1213     return rc;
1214 }
1215 
1216 namespace dcmi
1217 {
1218 namespace sensor_info
1219 {
1220 
1221 Response createFromJson(const Json& config)
1222 {
1223     Response response{};
1224     uint16_t recordId = config.value("record_id", 0);
1225     response.recordIdLsb = recordId & 0xFF;
1226     response.recordIdMsb = (recordId >> 8) & 0xFF;
1227     return response;
1228 }
1229 
1230 std::tuple<Response, NumInstances> read(const std::string& type,
1231                                         uint8_t instance, const Json& config)
1232 {
1233     Response response{};
1234 
1235     if (!instance)
1236     {
1237         log<level::ERR>("Expected non-zero instance");
1238         elog<InternalFailure>();
1239     }
1240 
1241     static const std::vector<Json> empty{};
1242     std::vector<Json> readings = config.value(type, empty);
1243     size_t numInstances = readings.size();
1244     for (const auto& reading : readings)
1245     {
1246         uint8_t instanceNum = reading.value("instance", 0);
1247         // Not the instance we're interested in
1248         if (instanceNum != instance)
1249         {
1250             continue;
1251         }
1252 
1253         response = createFromJson(reading);
1254 
1255         // Found the instance we're interested in
1256         break;
1257     }
1258 
1259     if (numInstances > maxInstances)
1260     {
1261         log<level::DEBUG>("Trimming IPMI num instances",
1262                           entry("NUM_INSTANCES=%d", numInstances));
1263         numInstances = maxInstances;
1264     }
1265     return std::make_tuple(response, numInstances);
1266 }
1267 
1268 std::tuple<ResponseList, NumInstances>
1269     readAll(const std::string& type, uint8_t instanceStart, const Json& config)
1270 {
1271     ResponseList responses{};
1272 
1273     size_t numInstances = 0;
1274     static const std::vector<Json> empty{};
1275     std::vector<Json> readings = config.value(type, empty);
1276     numInstances = readings.size();
1277     for (const auto& reading : readings)
1278     {
1279         try
1280         {
1281             // Max of 8 records
1282             if (responses.size() == maxRecords)
1283             {
1284                 break;
1285             }
1286 
1287             uint8_t instanceNum = reading.value("instance", 0);
1288             // Not in the instance range we're interested in
1289             if (instanceNum < instanceStart)
1290             {
1291                 continue;
1292             }
1293 
1294             Response response = createFromJson(reading);
1295             responses.push_back(response);
1296         }
1297         catch (std::exception& e)
1298         {
1299             log<level::DEBUG>(e.what());
1300             continue;
1301         }
1302     }
1303 
1304     if (numInstances > maxInstances)
1305     {
1306         log<level::DEBUG>("Trimming IPMI num instances",
1307                           entry("NUM_INSTANCES=%d", numInstances));
1308         numInstances = maxInstances;
1309     }
1310     return std::make_tuple(responses, numInstances);
1311 }
1312 
1313 } // namespace sensor_info
1314 } // namespace dcmi
1315 
1316 ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1317                          ipmi_request_t request, ipmi_response_t response,
1318                          ipmi_data_len_t data_len, ipmi_context_t context)
1319 {
1320     auto requestData =
1321         reinterpret_cast<const dcmi::GetSensorInfoRequest*>(request);
1322     auto responseData =
1323         reinterpret_cast<dcmi::GetSensorInfoResponseHdr*>(response);
1324 
1325     if (*data_len != sizeof(dcmi::GetSensorInfoRequest))
1326     {
1327         log<level::ERR>("Malformed request data",
1328                         entry("DATA_SIZE=%d", *data_len));
1329         return IPMI_CC_REQ_DATA_LEN_INVALID;
1330     }
1331     *data_len = 0;
1332 
1333     auto it = dcmi::entityIdToName.find(requestData->entityId);
1334     if (it == dcmi::entityIdToName.end())
1335     {
1336         log<level::ERR>("Unknown Entity ID",
1337                         entry("ENTITY_ID=%d", requestData->entityId));
1338         return IPMI_CC_INVALID_FIELD_REQUEST;
1339     }
1340 
1341     if (requestData->sensorType != dcmi::temperatureSensorType)
1342     {
1343         log<level::ERR>("Invalid sensor type",
1344                         entry("SENSOR_TYPE=%d", requestData->sensorType));
1345         return IPMI_CC_INVALID_FIELD_REQUEST;
1346     }
1347 
1348     dcmi::sensor_info::ResponseList sensors{};
1349     static dcmi::Json config{};
1350     static bool parsed = false;
1351 
1352     try
1353     {
1354         if (!parsed)
1355         {
1356             config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
1357             parsed = true;
1358         }
1359 
1360         if (!requestData->entityInstance)
1361         {
1362             // Read all instances
1363             std::tie(sensors, responseData->numInstances) =
1364                 dcmi::sensor_info::readAll(it->second,
1365                                            requestData->instanceStart, config);
1366         }
1367         else
1368         {
1369             // Read one instance
1370             sensors.resize(1);
1371             std::tie(sensors[0], responseData->numInstances) =
1372                 dcmi::sensor_info::read(it->second, requestData->entityInstance,
1373                                         config);
1374         }
1375         responseData->numRecords = sensors.size();
1376     }
1377     catch (InternalFailure& e)
1378     {
1379         return IPMI_CC_UNSPECIFIED_ERROR;
1380     }
1381 
1382     size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response);
1383     if (!sensors.empty())
1384     {
1385         memcpy(responseData + 1, // copy payload right after the response header
1386                sensors.data(), payloadSize);
1387     }
1388     *data_len = sizeof(dcmi::GetSensorInfoResponseHdr) + payloadSize;
1389 
1390     return IPMI_CC_OK;
1391 }
1392 
1393 void register_netfn_dcmi_functions()
1394 {
1395     // <Get Power Limit>
1396 
1397     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL,
1398                            getPowerLimit, PRIVILEGE_USER);
1399 
1400     // <Set Power Limit>
1401 
1402     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL,
1403                            setPowerLimit, PRIVILEGE_OPERATOR);
1404 
1405     // <Activate/Deactivate Power Limit>
1406 
1407     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT,
1408                            NULL, applyPowerLimit, PRIVILEGE_OPERATOR);
1409 
1410     // <Get Asset Tag>
1411 
1412     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL,
1413                            getAssetTag, PRIVILEGE_USER);
1414 
1415     // <Set Asset Tag>
1416 
1417     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL,
1418                            setAssetTag, PRIVILEGE_OPERATOR);
1419 
1420     // <Get Management Controller Identifier String>
1421 
1422     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR,
1423                            NULL, getMgmntCtrlIdStr, PRIVILEGE_USER);
1424 
1425     // <Set Management Controller Identifier String>
1426     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR,
1427                            NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN);
1428 
1429     // <Get DCMI capabilities>
1430     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES,
1431                            NULL, getDCMICapabilities, PRIVILEGE_USER);
1432 
1433     // <Get Temperature Readings>
1434     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS,
1435                            NULL, getTempReadings, PRIVILEGE_USER);
1436 
1437     // <Get Power Reading>
1438     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING,
1439                            NULL, getPowerReading, PRIVILEGE_USER);
1440 
1441     // <Get Sensor Info>
1442     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO, NULL,
1443                            getSensorInfo, PRIVILEGE_USER);
1444 
1445     // <Get DCMI Configuration Parameters>
1446     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS, NULL,
1447                            getDCMIConfParams, PRIVILEGE_USER);
1448 
1449     // <Set DCMI Configuration Parameters>
1450     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS, NULL,
1451                            setDCMIConfParams, PRIVILEGE_ADMIN);
1452 
1453     return;
1454 }
1455 // 956379
1456