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