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