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