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