1 #include "config.h"
2 
3 #include "dcmihandler.hpp"
4 
5 #include "user_channel/channel_layer.hpp"
6 #include "utils.hpp"
7 
8 #include <ipmid/api.h>
9 
10 #include <bitset>
11 #include <cmath>
12 #include <fstream>
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) > 8)
788         {
789             // Read the value corresponding to capability name and assign to
790             // 16bit bitset.
791             std::bitset<16> val(data.value(cap.name.c_str(), 0));
792             val <<= cap.position;
793             reinterpret_cast<uint16_t*>(
794                 responseData
795                     ->data)[(cap.bytePosition - 1) / sizeof(uint16_t)] |=
796                 val.to_ulong();
797         }
798         else
799         {
800             responseData->data[cap.bytePosition - 1] |=
801                 data.value(cap.name.c_str(), 0) << cap.position;
802         }
803     }
804 
805     responseData->groupID = dcmi::groupExtId;
806     responseData->major = DCMI_SPEC_MAJOR_VERSION;
807     responseData->minor = DCMI_SPEC_MINOR_VERSION;
808     responseData->paramRevision = DCMI_PARAMETER_REVISION;
809     *data_len = sizeof(*responseData) + caps->second.size;
810 
811     return IPMI_CC_OK;
812 }
813 
814 namespace dcmi
815 {
816 namespace temp_readings
817 {
818 
819 Temperature readTemp(const std::string& dbusService,
820                      const std::string& dbusPath)
821 {
822     // Read the temperature value from d-bus object. Need some conversion.
823     // As per the interface xyz.openbmc_project.Sensor.Value, the temperature
824     // is an double and in degrees C. It needs to be scaled by using the
825     // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t,
826     // with a separate single bit for the sign.
827 
828     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
829     auto result = ipmi::getAllDbusProperties(
830         bus, dbusService, dbusPath, "xyz.openbmc_project.Sensor.Value");
831     auto temperature = sdbusplus::message::variant_ns::visit(
832         ipmi::VariantToDoubleVisitor(), result.at("Value"));
833     double absTemp = std::abs(temperature);
834 
835     auto findFactor = result.find("Scale");
836     double factor = 0.0;
837     if (findFactor != result.end())
838     {
839         factor = sdbusplus::message::variant_ns::visit(
840             ipmi::VariantToDoubleVisitor(), findFactor->second);
841     }
842     double scale = std::pow(10, factor);
843 
844     auto tempDegrees = absTemp * scale;
845     // Max absolute temp as per ipmi spec is 128.
846     if (tempDegrees > maxTemp)
847     {
848         tempDegrees = maxTemp;
849     }
850 
851     return std::make_tuple(static_cast<uint8_t>(tempDegrees),
852                            (temperature < 0));
853 }
854 
855 std::tuple<Response, NumInstances> read(const std::string& type,
856                                         uint8_t instance)
857 {
858     Response response{};
859     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
860 
861     if (!instance)
862     {
863         log<level::ERR>("Expected non-zero instance");
864         elog<InternalFailure>();
865     }
866 
867     auto data = parseJSONConfig(gDCMISensorsConfig);
868     static const std::vector<Json> empty{};
869     std::vector<Json> readings = data.value(type, empty);
870     size_t numInstances = readings.size();
871     for (const auto& j : readings)
872     {
873         uint8_t instanceNum = j.value("instance", 0);
874         // Not the instance we're interested in
875         if (instanceNum != instance)
876         {
877             continue;
878         }
879 
880         std::string path = j.value("dbus", "");
881         std::string service;
882         try
883         {
884             service =
885                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
886         }
887         catch (std::exception& e)
888         {
889             log<level::DEBUG>(e.what());
890             return std::make_tuple(response, numInstances);
891         }
892 
893         response.instance = instance;
894         uint8_t temp{};
895         bool sign{};
896         std::tie(temp, sign) = readTemp(service, path);
897         response.temperature = temp;
898         response.sign = sign;
899 
900         // Found the instance we're interested in
901         break;
902     }
903 
904     if (numInstances > maxInstances)
905     {
906         numInstances = maxInstances;
907     }
908     return std::make_tuple(response, numInstances);
909 }
910 
911 std::tuple<ResponseList, NumInstances> readAll(const std::string& type,
912                                                uint8_t instanceStart)
913 {
914     ResponseList response{};
915     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
916 
917     size_t numInstances = 0;
918     auto data = parseJSONConfig(gDCMISensorsConfig);
919     static const std::vector<Json> empty{};
920     std::vector<Json> readings = data.value(type, empty);
921     numInstances = readings.size();
922     for (const auto& j : readings)
923     {
924         try
925         {
926             // Max of 8 response data sets
927             if (response.size() == maxDataSets)
928             {
929                 break;
930             }
931 
932             uint8_t instanceNum = j.value("instance", 0);
933             // Not in the instance range we're interested in
934             if (instanceNum < instanceStart)
935             {
936                 continue;
937             }
938 
939             std::string path = j.value("dbus", "");
940             auto service =
941                 ipmi::getService(bus, "xyz.openbmc_project.Sensor.Value", path);
942 
943             Response r{};
944             r.instance = instanceNum;
945             uint8_t temp{};
946             bool sign{};
947             std::tie(temp, sign) = readTemp(service, path);
948             r.temperature = temp;
949             r.sign = sign;
950             response.push_back(r);
951         }
952         catch (std::exception& e)
953         {
954             log<level::DEBUG>(e.what());
955             continue;
956         }
957     }
958 
959     if (numInstances > maxInstances)
960     {
961         numInstances = maxInstances;
962     }
963     return std::make_tuple(response, numInstances);
964 }
965 
966 } // namespace temp_readings
967 } // namespace dcmi
968 
969 ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
970                            ipmi_request_t request, ipmi_response_t response,
971                            ipmi_data_len_t data_len, ipmi_context_t context)
972 {
973     auto requestData =
974         reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request);
975     auto responseData =
976         reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response);
977 
978     if (*data_len != sizeof(dcmi::GetTempReadingsRequest))
979     {
980         log<level::ERR>("Malformed request data",
981                         entry("DATA_SIZE=%d", *data_len));
982         return IPMI_CC_REQ_DATA_LEN_INVALID;
983     }
984     *data_len = 0;
985 
986     auto it = dcmi::entityIdToName.find(requestData->entityId);
987     if (it == dcmi::entityIdToName.end())
988     {
989         log<level::ERR>("Unknown Entity ID",
990                         entry("ENTITY_ID=%d", requestData->entityId));
991         return IPMI_CC_INVALID_FIELD_REQUEST;
992     }
993 
994     if (requestData->groupID != dcmi::groupExtId)
995     {
996         log<level::ERR>("Invalid Group ID",
997                         entry("GROUP_ID=%d", requestData->groupID));
998         return IPMI_CC_INVALID_FIELD_REQUEST;
999     }
1000 
1001     if (requestData->sensorType != dcmi::temperatureSensorType)
1002     {
1003         log<level::ERR>("Invalid sensor type",
1004                         entry("SENSOR_TYPE=%d", requestData->sensorType));
1005         return IPMI_CC_INVALID_FIELD_REQUEST;
1006     }
1007 
1008     dcmi::temp_readings::ResponseList temps{};
1009     try
1010     {
1011         if (!requestData->entityInstance)
1012         {
1013             // Read all instances
1014             std::tie(temps, responseData->numInstances) =
1015                 dcmi::temp_readings::readAll(it->second,
1016                                              requestData->instanceStart);
1017         }
1018         else
1019         {
1020             // Read one instance
1021             temps.resize(1);
1022             std::tie(temps[0], responseData->numInstances) =
1023                 dcmi::temp_readings::read(it->second,
1024                                           requestData->entityInstance);
1025         }
1026         responseData->numDataSets = temps.size();
1027     }
1028     catch (InternalFailure& e)
1029     {
1030         return IPMI_CC_UNSPECIFIED_ERROR;
1031     }
1032 
1033     responseData->groupID = dcmi::groupExtId;
1034     size_t payloadSize = temps.size() * sizeof(dcmi::temp_readings::Response);
1035     if (!temps.empty())
1036     {
1037         memcpy(responseData + 1, // copy payload right after the response header
1038                temps.data(), payloadSize);
1039     }
1040     *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize;
1041 
1042     return IPMI_CC_OK;
1043 }
1044 
1045 int64_t getPowerReading(sdbusplus::bus::bus& bus)
1046 {
1047     std::ifstream sensorFile(POWER_READING_SENSOR);
1048     std::string objectPath;
1049     if (!sensorFile.is_open())
1050     {
1051         log<level::ERR>("Power reading configuration file not found",
1052                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1053         elog<InternalFailure>();
1054     }
1055 
1056     auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1057     if (data.is_discarded())
1058     {
1059         log<level::ERR>("Error in parsing configuration file",
1060                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1061         elog<InternalFailure>();
1062     }
1063 
1064     objectPath = data.value("path", "");
1065     if (objectPath.empty())
1066     {
1067         log<level::ERR>("Power sensor D-Bus object path is empty",
1068                         entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1069         elog<InternalFailure>();
1070     }
1071 
1072     // Return default value if failed to read from D-Bus object
1073     int64_t power = 0;
1074     try
1075     {
1076         auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath);
1077 
1078         // Read the sensor value and scale properties
1079         auto properties = ipmi::getAllDbusProperties(bus, service, objectPath,
1080                                                      SENSOR_VALUE_INTF);
1081         auto value = variant_ns::get<int64_t>(properties[SENSOR_VALUE_PROP]);
1082         auto scale = variant_ns::get<int64_t>(properties[SENSOR_SCALE_PROP]);
1083 
1084         // Power reading needs to be scaled with the Scale value using the
1085         // formula Value * 10^Scale.
1086         power = value * std::pow(10, scale);
1087     }
1088     catch (std::exception& e)
1089     {
1090         log<level::INFO>("Failure to read power value from D-Bus object",
1091                          entry("OBJECT_PATH=%s", objectPath.c_str()),
1092                          entry("INTERFACE=%s", SENSOR_VALUE_INTF));
1093     }
1094     return power;
1095 }
1096 
1097 ipmi_ret_t setDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1098                              ipmi_request_t request, ipmi_response_t response,
1099                              ipmi_data_len_t data_len, ipmi_context_t context)
1100 {
1101     auto requestData =
1102         reinterpret_cast<const dcmi::SetConfParamsRequest*>(request);
1103     auto responseData =
1104         reinterpret_cast<dcmi::SetConfParamsResponse*>(response);
1105 
1106     if (requestData->groupID != dcmi::groupExtId ||
1107         *data_len < DCMI_SET_CONF_PARAM_REQ_PACKET_MIN_SIZE ||
1108         *data_len > DCMI_SET_CONF_PARAM_REQ_PACKET_MAX_SIZE)
1109     {
1110         log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
1111                         entry("GROUP_ID=%d", requestData->groupID),
1112                         entry("PACKET SIZE=%d", *data_len));
1113         return IPMI_CC_INVALID_FIELD_REQUEST;
1114     }
1115 
1116     *data_len = 0;
1117 
1118     try
1119     {
1120         // Take action based on the Parameter Selector
1121         switch (
1122             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1123         {
1124             case dcmi::DCMIConfigParameters::ActivateDHCP:
1125 
1126                 if ((requestData->data[0] & DCMI_ACTIVATE_DHCP_MASK) &&
1127                     dcmi::getDHCPEnabled())
1128                 {
1129                     // When these conditions are met we have to trigger DHCP
1130                     // protocol restart using the latest parameter settings, but
1131                     // as per n/w manager design, each time when we update n/w
1132                     // parameters, n/w service is restarted. So we no need to
1133                     // take any action in this case.
1134                 }
1135                 break;
1136 
1137             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1138 
1139                 if (requestData->data[0] & DCMI_OPTION_12_MASK)
1140                 {
1141                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, true);
1142                 }
1143                 else
1144                 {
1145                     dcmi::setDHCPOption(DHCP_OPT12_ENABLED, false);
1146                 }
1147 
1148                 // Systemd-networkd doesn't support Random Back off
1149                 if (requestData->data[0] & DCMI_RAND_BACK_OFF_MASK)
1150                 {
1151                     return IPMI_CC_INVALID;
1152                 }
1153                 break;
1154             // Systemd-networkd doesn't allow to configure DHCP timigs
1155             case dcmi::DCMIConfigParameters::DHCPTiming1:
1156             case dcmi::DCMIConfigParameters::DHCPTiming2:
1157             case dcmi::DCMIConfigParameters::DHCPTiming3:
1158             default:
1159                 return IPMI_CC_INVALID;
1160         }
1161     }
1162     catch (std::exception& e)
1163     {
1164         log<level::ERR>(e.what());
1165         return IPMI_CC_UNSPECIFIED_ERROR;
1166     }
1167     responseData->groupID = dcmi::groupExtId;
1168     *data_len = sizeof(dcmi::SetConfParamsResponse);
1169 
1170     return IPMI_CC_OK;
1171 }
1172 
1173 ipmi_ret_t getDCMIConfParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1174                              ipmi_request_t request, ipmi_response_t response,
1175                              ipmi_data_len_t data_len, ipmi_context_t context)
1176 {
1177 
1178     auto requestData =
1179         reinterpret_cast<const dcmi::GetConfParamsRequest*>(request);
1180     auto responseData =
1181         reinterpret_cast<dcmi::GetConfParamsResponse*>(response);
1182 
1183     responseData->data[0] = 0x00;
1184 
1185     if (requestData->groupID != dcmi::groupExtId ||
1186         *data_len != sizeof(dcmi::GetConfParamsRequest))
1187     {
1188         log<level::ERR>("Invalid Group ID or Invalid Requested Packet size",
1189                         entry("GROUP_ID=%d", requestData->groupID),
1190                         entry("PACKET SIZE=%d", *data_len));
1191         return IPMI_CC_INVALID_FIELD_REQUEST;
1192     }
1193 
1194     *data_len = 0;
1195 
1196     try
1197     {
1198         // Take action based on the Parameter Selector
1199         switch (
1200             static_cast<dcmi::DCMIConfigParameters>(requestData->paramSelect))
1201         {
1202             case dcmi::DCMIConfigParameters::ActivateDHCP:
1203                 responseData->data[0] = DCMI_ACTIVATE_DHCP_REPLY;
1204                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1205                 break;
1206             case dcmi::DCMIConfigParameters::DiscoveryConfig:
1207                 if (dcmi::getDHCPOption(DHCP_OPT12_ENABLED))
1208                 {
1209                     responseData->data[0] |= DCMI_OPTION_12_MASK;
1210                 }
1211                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1212                 break;
1213             // Get below values from Systemd-networkd source code
1214             case dcmi::DCMIConfigParameters::DHCPTiming1:
1215                 responseData->data[0] = DHCP_TIMING1;
1216                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 1;
1217                 break;
1218             case dcmi::DCMIConfigParameters::DHCPTiming2:
1219                 responseData->data[0] = DHCP_TIMING2_LOWER;
1220                 responseData->data[1] = DHCP_TIMING2_UPPER;
1221                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1222                 break;
1223             case dcmi::DCMIConfigParameters::DHCPTiming3:
1224                 responseData->data[0] = DHCP_TIMING3_LOWER;
1225                 responseData->data[1] = DHCP_TIMING3_UPPER;
1226                 *data_len = sizeof(dcmi::GetConfParamsResponse) + 2;
1227                 break;
1228             default:
1229                 *data_len = 0;
1230                 return IPMI_CC_INVALID;
1231         }
1232     }
1233     catch (std::exception& e)
1234     {
1235         log<level::ERR>(e.what());
1236         return IPMI_CC_UNSPECIFIED_ERROR;
1237     }
1238 
1239     responseData->groupID = dcmi::groupExtId;
1240     responseData->major = DCMI_SPEC_MAJOR_VERSION;
1241     responseData->minor = DCMI_SPEC_MINOR_VERSION;
1242     responseData->paramRevision = DCMI_CONFIG_PARAMETER_REVISION;
1243 
1244     return IPMI_CC_OK;
1245 }
1246 
1247 ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1248                            ipmi_request_t request, ipmi_response_t response,
1249                            ipmi_data_len_t data_len, ipmi_context_t context)
1250 {
1251     if (!dcmi::isDCMIPowerMgmtSupported())
1252     {
1253         *data_len = 0;
1254         log<level::ERR>("DCMI Power management is unsupported!");
1255         return IPMI_CC_INVALID;
1256     }
1257 
1258     ipmi_ret_t rc = IPMI_CC_OK;
1259     auto requestData =
1260         reinterpret_cast<const dcmi::GetPowerReadingRequest*>(request);
1261     auto responseData =
1262         reinterpret_cast<dcmi::GetPowerReadingResponse*>(response);
1263 
1264     if (requestData->groupID != dcmi::groupExtId)
1265     {
1266         *data_len = 0;
1267         return IPMI_CC_INVALID_FIELD_REQUEST;
1268     }
1269 
1270     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
1271     int64_t power = 0;
1272     try
1273     {
1274         power = getPowerReading(bus);
1275     }
1276     catch (InternalFailure& e)
1277     {
1278         log<level::ERR>("Error in reading power sensor value",
1279                         entry("INTERFACE=%s", SENSOR_VALUE_INTF),
1280                         entry("PROPERTY=%s", SENSOR_VALUE_PROP));
1281         return IPMI_CC_UNSPECIFIED_ERROR;
1282     }
1283     responseData->groupID = dcmi::groupExtId;
1284 
1285     // TODO: openbmc/openbmc#2819
1286     // Minimum, Maximum, Average power, TimeFrame, TimeStamp,
1287     // PowerReadingState readings need to be populated
1288     // after Telemetry changes.
1289     uint16_t totalPower = static_cast<uint16_t>(power);
1290     responseData->currentPower = totalPower;
1291     responseData->minimumPower = totalPower;
1292     responseData->maximumPower = totalPower;
1293     responseData->averagePower = totalPower;
1294 
1295     *data_len = sizeof(*responseData);
1296     return rc;
1297 }
1298 
1299 namespace dcmi
1300 {
1301 namespace sensor_info
1302 {
1303 
1304 Response createFromJson(const Json& config)
1305 {
1306     Response response{};
1307     uint16_t recordId = config.value("record_id", 0);
1308     response.recordIdLsb = recordId & 0xFF;
1309     response.recordIdMsb = (recordId >> 8) & 0xFF;
1310     return response;
1311 }
1312 
1313 std::tuple<Response, NumInstances> read(const std::string& type,
1314                                         uint8_t instance, const Json& config)
1315 {
1316     Response response{};
1317 
1318     if (!instance)
1319     {
1320         log<level::ERR>("Expected non-zero instance");
1321         elog<InternalFailure>();
1322     }
1323 
1324     static const std::vector<Json> empty{};
1325     std::vector<Json> readings = config.value(type, empty);
1326     size_t numInstances = readings.size();
1327     for (const auto& reading : readings)
1328     {
1329         uint8_t instanceNum = reading.value("instance", 0);
1330         // Not the instance we're interested in
1331         if (instanceNum != instance)
1332         {
1333             continue;
1334         }
1335 
1336         response = createFromJson(reading);
1337 
1338         // Found the instance we're interested in
1339         break;
1340     }
1341 
1342     if (numInstances > maxInstances)
1343     {
1344         log<level::DEBUG>("Trimming IPMI num instances",
1345                           entry("NUM_INSTANCES=%d", numInstances));
1346         numInstances = maxInstances;
1347     }
1348     return std::make_tuple(response, numInstances);
1349 }
1350 
1351 std::tuple<ResponseList, NumInstances>
1352     readAll(const std::string& type, uint8_t instanceStart, const Json& config)
1353 {
1354     ResponseList responses{};
1355 
1356     size_t numInstances = 0;
1357     static const std::vector<Json> empty{};
1358     std::vector<Json> readings = config.value(type, empty);
1359     numInstances = readings.size();
1360     for (const auto& reading : readings)
1361     {
1362         try
1363         {
1364             // Max of 8 records
1365             if (responses.size() == maxRecords)
1366             {
1367                 break;
1368             }
1369 
1370             uint8_t instanceNum = reading.value("instance", 0);
1371             // Not in the instance range we're interested in
1372             if (instanceNum < instanceStart)
1373             {
1374                 continue;
1375             }
1376 
1377             Response response = createFromJson(reading);
1378             responses.push_back(response);
1379         }
1380         catch (std::exception& e)
1381         {
1382             log<level::DEBUG>(e.what());
1383             continue;
1384         }
1385     }
1386 
1387     if (numInstances > maxInstances)
1388     {
1389         log<level::DEBUG>("Trimming IPMI num instances",
1390                           entry("NUM_INSTANCES=%d", numInstances));
1391         numInstances = maxInstances;
1392     }
1393     return std::make_tuple(responses, numInstances);
1394 }
1395 
1396 } // namespace sensor_info
1397 } // namespace dcmi
1398 
1399 ipmi_ret_t getSensorInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1400                          ipmi_request_t request, ipmi_response_t response,
1401                          ipmi_data_len_t data_len, ipmi_context_t context)
1402 {
1403     auto requestData =
1404         reinterpret_cast<const dcmi::GetSensorInfoRequest*>(request);
1405     auto responseData =
1406         reinterpret_cast<dcmi::GetSensorInfoResponseHdr*>(response);
1407 
1408     if (*data_len != sizeof(dcmi::GetSensorInfoRequest))
1409     {
1410         log<level::ERR>("Malformed request data",
1411                         entry("DATA_SIZE=%d", *data_len));
1412         return IPMI_CC_REQ_DATA_LEN_INVALID;
1413     }
1414     *data_len = 0;
1415 
1416     auto it = dcmi::entityIdToName.find(requestData->entityId);
1417     if (it == dcmi::entityIdToName.end())
1418     {
1419         log<level::ERR>("Unknown Entity ID",
1420                         entry("ENTITY_ID=%d", requestData->entityId));
1421         return IPMI_CC_INVALID_FIELD_REQUEST;
1422     }
1423 
1424     if (requestData->groupID != dcmi::groupExtId)
1425     {
1426         log<level::ERR>("Invalid Group ID",
1427                         entry("GROUP_ID=%d", requestData->groupID));
1428         return IPMI_CC_INVALID_FIELD_REQUEST;
1429     }
1430 
1431     if (requestData->sensorType != dcmi::temperatureSensorType)
1432     {
1433         log<level::ERR>("Invalid sensor type",
1434                         entry("SENSOR_TYPE=%d", requestData->sensorType));
1435         return IPMI_CC_INVALID_FIELD_REQUEST;
1436     }
1437 
1438     dcmi::sensor_info::ResponseList sensors{};
1439     static dcmi::Json config{};
1440     static bool parsed = false;
1441 
1442     try
1443     {
1444         if (!parsed)
1445         {
1446             config = dcmi::parseJSONConfig(dcmi::gDCMISensorsConfig);
1447             parsed = true;
1448         }
1449 
1450         if (!requestData->entityInstance)
1451         {
1452             // Read all instances
1453             std::tie(sensors, responseData->numInstances) =
1454                 dcmi::sensor_info::readAll(it->second,
1455                                            requestData->instanceStart, config);
1456         }
1457         else
1458         {
1459             // Read one instance
1460             sensors.resize(1);
1461             std::tie(sensors[0], responseData->numInstances) =
1462                 dcmi::sensor_info::read(it->second, requestData->entityInstance,
1463                                         config);
1464         }
1465         responseData->numRecords = sensors.size();
1466     }
1467     catch (InternalFailure& e)
1468     {
1469         return IPMI_CC_UNSPECIFIED_ERROR;
1470     }
1471 
1472     responseData->groupID = dcmi::groupExtId;
1473     size_t payloadSize = sensors.size() * sizeof(dcmi::sensor_info::Response);
1474     if (!sensors.empty())
1475     {
1476         memcpy(responseData + 1, // copy payload right after the response header
1477                sensors.data(), payloadSize);
1478     }
1479     *data_len = sizeof(dcmi::GetSensorInfoResponseHdr) + payloadSize;
1480 
1481     return IPMI_CC_OK;
1482 }
1483 
1484 void register_netfn_dcmi_functions()
1485 {
1486     // <Get Power Limit>
1487 
1488     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT, NULL,
1489                            getPowerLimit, PRIVILEGE_USER);
1490 
1491     // <Set Power Limit>
1492 
1493     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT, NULL,
1494                            setPowerLimit, PRIVILEGE_OPERATOR);
1495 
1496     // <Activate/Deactivate Power Limit>
1497 
1498     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT,
1499                            NULL, applyPowerLimit, PRIVILEGE_OPERATOR);
1500 
1501     // <Get Asset Tag>
1502 
1503     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG, NULL,
1504                            getAssetTag, PRIVILEGE_USER);
1505 
1506     // <Set Asset Tag>
1507 
1508     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG, NULL,
1509                            setAssetTag, PRIVILEGE_OPERATOR);
1510 
1511     // <Get Management Controller Identifier String>
1512 
1513     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR,
1514                            NULL, getMgmntCtrlIdStr, PRIVILEGE_USER);
1515 
1516     // <Set Management Controller Identifier String>
1517     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR,
1518                            NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN);
1519 
1520     // <Get DCMI capabilities>
1521     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES,
1522                            NULL, getDCMICapabilities, PRIVILEGE_USER);
1523 
1524     // <Get Temperature Readings>
1525     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS,
1526                            NULL, getTempReadings, PRIVILEGE_USER);
1527 
1528     // <Get Power Reading>
1529     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING,
1530                            NULL, getPowerReading, PRIVILEGE_USER);
1531 
1532     // <Get Sensor Info>
1533     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_SENSOR_INFO, NULL,
1534                            getSensorInfo, PRIVILEGE_USER);
1535 
1536     // <Get DCMI Configuration Parameters>
1537     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CONF_PARAMS, NULL,
1538                            getDCMIConfParams, PRIVILEGE_USER);
1539 
1540     // <Set DCMI Configuration Parameters>
1541     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_CONF_PARAMS, NULL,
1542                            setDCMIConfParams, PRIVILEGE_ADMIN);
1543 
1544     return;
1545 }
1546 // 956379
1547