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