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