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