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