blob: 11befdc490a4afa53ec5d1bb320fb68b1e5e77b8 [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"
Tom Josephbe5eaa12017-07-12 19:54:44 +053016
17using namespace phosphor::logging;
18using InternalFailure =
19 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Chris Austen1810bec2015-10-13 12:12:39 -050020
21void register_netfn_dcmi_functions() __attribute__((constructor));
22
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050023constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
24constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
25
26constexpr auto POWER_CAP_PROP = "PowerCap";
27constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
28
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -060029constexpr auto DCMI_PARAMETER_REVISION = 2;
30constexpr auto DCMI_SPEC_MAJOR_VERSION = 1;
31constexpr auto DCMI_SPEC_MINOR_VERSION = 5;
32constexpr auto DCMI_CAP_JSON_FILE = "/usr/share/ipmi-providers/dcmi_cap.json";
33
Marri Devender Rao66c5fda2018-01-18 10:48:37 -060034constexpr auto SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value";
35constexpr auto SENSOR_VALUE_PROP = "Value";
36constexpr auto SENSOR_SCALE_PROP = "Scale";
37
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050038using namespace phosphor::logging;
39
Tom Josephb9d86f42017-07-26 18:03:47 +053040namespace dcmi
41{
42
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050043uint32_t getPcap(sdbusplus::bus::bus& bus)
44{
45 auto settingService = ipmi::getService(bus,
Andrew Geisslera944d432017-07-19 17:53:44 -050046 PCAP_INTERFACE,PCAP_PATH);
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050047
48 auto method = bus.new_method_call(settingService.c_str(),
49 PCAP_PATH,
50 "org.freedesktop.DBus.Properties",
51 "Get");
52
53 method.append(PCAP_INTERFACE, POWER_CAP_PROP);
54 auto reply = bus.call(method);
55
56 if (reply.is_method_error())
57 {
58 log<level::ERR>("Error in getPcap prop");
Tom Josephb9d86f42017-07-26 18:03:47 +053059 elog<InternalFailure>();
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050060 }
61 sdbusplus::message::variant<uint32_t> pcap;
62 reply.read(pcap);
63
Tom Josephb9d86f42017-07-26 18:03:47 +053064 return pcap.get<uint32_t>();
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050065}
66
67bool getPcapEnabled(sdbusplus::bus::bus& bus)
68{
69 auto settingService = ipmi::getService(bus,
Andrew Geisslera944d432017-07-19 17:53:44 -050070 PCAP_INTERFACE,PCAP_PATH);
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050071
72 auto method = bus.new_method_call(settingService.c_str(),
73 PCAP_PATH,
74 "org.freedesktop.DBus.Properties",
75 "Get");
76
77 method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
78 auto reply = bus.call(method);
79
80 if (reply.is_method_error())
81 {
82 log<level::ERR>("Error in getPcapEnabled prop");
Tom Josephb9d86f42017-07-26 18:03:47 +053083 elog<InternalFailure>();
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050084 }
85 sdbusplus::message::variant<bool> pcapEnabled;
86 reply.read(pcapEnabled);
87
Tom Josephb9d86f42017-07-26 18:03:47 +053088 return pcapEnabled.get<bool>();
Andrew Geissler50c0c8f2017-07-11 16:18:51 -050089}
Chris Austen1810bec2015-10-13 12:12:39 -050090
Tom Joseph46fa37d2017-07-26 18:11:55 +053091void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
92{
93 auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
94
95 auto method = bus.new_method_call(service.c_str(),
96 PCAP_PATH,
97 "org.freedesktop.DBus.Properties",
98 "Set");
99
100 method.append(PCAP_INTERFACE, POWER_CAP_PROP);
101 method.append(sdbusplus::message::variant<uint32_t>(powerCap));
102
103 auto reply = bus.call(method);
104
105 if (reply.is_method_error())
106 {
107 log<level::ERR>("Error in setPcap property");
108 elog<InternalFailure>();
109 }
110}
111
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530112void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled)
113{
114 auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
115
116 auto method = bus.new_method_call(service.c_str(),
117 PCAP_PATH,
118 "org.freedesktop.DBus.Properties",
119 "Set");
120
121 method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
122 method.append(sdbusplus::message::variant<bool>(enabled));
123
124 auto reply = bus.call(method);
125
126 if (reply.is_method_error())
127 {
128 log<level::ERR>("Error in setPcapEnabled property");
129 elog<InternalFailure>();
130 }
131}
132
Tom Josephbe5eaa12017-07-12 19:54:44 +0530133void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree)
134{
135 static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
136 static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
137 static constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
138 static constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory/";
139
140 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
141 auto depth = 0;
142
143 auto mapperCall = bus.new_method_call(mapperBusName,
144 mapperObjPath,
145 mapperIface,
146 "GetSubTree");
147
148 mapperCall.append(inventoryRoot);
149 mapperCall.append(depth);
150 mapperCall.append(std::vector<std::string>({dcmi::assetTagIntf}));
151
152 auto mapperReply = bus.call(mapperCall);
153 if (mapperReply.is_method_error())
154 {
155 log<level::ERR>("Error in mapper call");
156 elog<InternalFailure>();
157 }
158
159 mapperReply.read(objectTree);
160
161 if (objectTree.empty())
162 {
163 log<level::ERR>("AssetTag property is not populated");
164 elog<InternalFailure>();
165 }
166}
167
168std::string readAssetTag()
169{
170 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
171 dcmi::assettag::ObjectTree objectTree;
172
173 // Read the object tree with the inventory root to figure out the object
174 // that has implemented the Asset tag interface.
175 readAssetTagObjectTree(objectTree);
176
177 auto method = bus.new_method_call(
178 (objectTree.begin()->second.begin()->first).c_str(),
179 (objectTree.begin()->first).c_str(),
180 dcmi::propIntf,
181 "Get");
182 method.append(dcmi::assetTagIntf);
183 method.append(dcmi::assetTagProp);
184
185 auto reply = bus.call(method);
186 if (reply.is_method_error())
187 {
188 log<level::ERR>("Error in reading asset tag");
189 elog<InternalFailure>();
190 }
191
192 sdbusplus::message::variant<std::string> assetTag;
193 reply.read(assetTag);
194
195 return assetTag.get<std::string>();
196}
197
Tom Josephbe5b9892017-07-15 00:55:23 +0530198void writeAssetTag(const std::string& assetTag)
199{
200 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
201 dcmi::assettag::ObjectTree objectTree;
202
203 // Read the object tree with the inventory root to figure out the object
204 // that has implemented the Asset tag interface.
205 readAssetTagObjectTree(objectTree);
206
207 auto method = bus.new_method_call(
208 (objectTree.begin()->second.begin()->first).c_str(),
209 (objectTree.begin()->first).c_str(),
210 dcmi::propIntf,
211 "Set");
212 method.append(dcmi::assetTagIntf);
213 method.append(dcmi::assetTagProp);
214 method.append(sdbusplus::message::variant<std::string>(assetTag));
215
216 auto reply = bus.call(method);
217 if (reply.is_method_error())
218 {
219 log<level::ERR>("Error in writing asset tag");
220 elog<InternalFailure>();
221 }
222}
223
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300224std::string getHostName(void)
225{
226 sdbusplus::bus::bus bus{ ipmid_get_sd_bus_connection() };
227
228 auto service = ipmi::getService(bus, networkConfigIntf, networkConfigObj);
229 auto value = ipmi::getDbusProperty(bus, service,
230 networkConfigObj, networkConfigIntf, hostNameProp);
231
232 return value.get<std::string>();
233}
234
Tom Josephbe5eaa12017-07-12 19:54:44 +0530235} // namespace dcmi
Chris Austen1810bec2015-10-13 12:12:39 -0500236
Tom Josephb9d86f42017-07-26 18:03:47 +0530237ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
238 ipmi_request_t request, ipmi_response_t response,
239 ipmi_data_len_t data_len, ipmi_context_t context)
240{
241 auto requestData = reinterpret_cast<const dcmi::GetPowerLimitRequest*>
242 (request);
243 std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
244 auto responseData = reinterpret_cast<dcmi::GetPowerLimitResponse*>
245 (outPayload.data());
246
247 if (requestData->groupID != dcmi::groupExtId)
248 {
249 *data_len = 0;
250 return IPMI_CC_INVALID_FIELD_REQUEST;
251 }
252
253 sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
254 uint32_t pcapValue = 0;
255 bool pcapEnable = false;
256
257 try
258 {
259 pcapValue = dcmi::getPcap(sdbus);
260 pcapEnable = dcmi::getPcapEnabled(sdbus);
261 }
262 catch (InternalFailure& e)
263 {
264 *data_len = 0;
265 return IPMI_CC_UNSPECIFIED_ERROR;
266 }
267
268 responseData->groupID = dcmi::groupExtId;
269
270 /*
271 * Exception action if power limit is exceeded and cannot be controlled
272 * with the correction time limit is hardcoded to Hard Power Off system
273 * and log event to SEL.
274 */
275 constexpr auto exception = 0x01;
276 responseData->exceptionAction = exception;
277
278 responseData->powerLimit = static_cast<uint16_t>(pcapValue);
279
280 /*
281 * Correction time limit and Statistics sampling period is currently not
282 * populated.
283 */
284
285 *data_len = outPayload.size();
286 memcpy(response, outPayload.data(), *data_len);
287
288 if (pcapEnable)
289 {
290 return IPMI_CC_OK;
291 }
292 else
293 {
294 return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT;
295 }
296}
297
Tom Joseph46fa37d2017-07-26 18:11:55 +0530298ipmi_ret_t setPowerLimit(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 = reinterpret_cast<const dcmi::SetPowerLimitRequest*>
303 (request);
304 std::vector<uint8_t> outPayload(sizeof(dcmi::SetPowerLimitResponse));
305 auto responseData = reinterpret_cast<dcmi::SetPowerLimitResponse*>
306 (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
316 // Only process the power limit requested in watts.
317 try
318 {
319 dcmi::setPcap(sdbus, requestData->powerLimit);
320 }
321 catch (InternalFailure& e)
322 {
323 *data_len = 0;
324 return IPMI_CC_UNSPECIFIED_ERROR;
325 }
326
327 log<level::INFO>("Set Power Cap",
328 entry("POWERCAP=%u", requestData->powerLimit));
329
330 responseData->groupID = dcmi::groupExtId;
331 memcpy(response, outPayload.data(), outPayload.size());
332 *data_len = outPayload.size();
333
334 return IPMI_CC_OK;
335}
336
Tom Joseph6c8d51b2017-07-26 18:18:06 +0530337ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
338 ipmi_request_t request, ipmi_response_t response,
339 ipmi_data_len_t data_len, ipmi_context_t context)
340{
341 auto requestData = reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>
342 (request);
343 std::vector<uint8_t> outPayload(sizeof(dcmi::ApplyPowerLimitResponse));
344 auto responseData = reinterpret_cast<dcmi::ApplyPowerLimitResponse*>
345 (outPayload.data());
346
347 if (requestData->groupID != dcmi::groupExtId)
348 {
349 *data_len = 0;
350 return IPMI_CC_INVALID_FIELD_REQUEST;
351 }
352
353 sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
354
355 try
356 {
357 dcmi::setPcapEnable(sdbus,
358 static_cast<bool>(requestData->powerLimitAction));
359 }
360 catch (InternalFailure& e)
361 {
362 *data_len = 0;
363 return IPMI_CC_UNSPECIFIED_ERROR;
364 }
365
366 log<level::INFO>("Set Power Cap Enable",
367 entry("POWERCAPENABLE=%u", requestData->powerLimitAction));
368
369 responseData->groupID = dcmi::groupExtId;
370 memcpy(response, outPayload.data(), outPayload.size());
371 *data_len = outPayload.size();
372
373 return IPMI_CC_OK;
374}
375
Tom Joseph6f6dd4d2017-07-12 20:07:11 +0530376ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
377 ipmi_request_t request, ipmi_response_t response,
378 ipmi_data_len_t data_len, ipmi_context_t context)
379{
380 auto requestData = reinterpret_cast<const dcmi::GetAssetTagRequest*>
381 (request);
382 std::vector<uint8_t> outPayload(sizeof(dcmi::GetAssetTagResponse));
383 auto responseData = reinterpret_cast<dcmi::GetAssetTagResponse*>
384 (outPayload.data());
385
386 if (requestData->groupID != dcmi::groupExtId)
387 {
388 *data_len = 0;
389 return IPMI_CC_INVALID_FIELD_REQUEST;
390 }
391
392 // Verify offset to read and number of bytes to read are not exceeding the
393 // range.
394 if ((requestData->offset > dcmi::assetTagMaxOffset) ||
395 (requestData->bytes > dcmi::maxBytes) ||
396 ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
397 {
398 *data_len = 0;
399 return IPMI_CC_PARM_OUT_OF_RANGE;
400 }
401
402 std::string assetTag;
403
404 try
405 {
406 assetTag = dcmi::readAssetTag();
407 }
408 catch (InternalFailure& e)
409 {
410 *data_len = 0;
411 return IPMI_CC_UNSPECIFIED_ERROR;
412 }
413
414 responseData->groupID = dcmi::groupExtId;
415
416 // Return if the asset tag is not populated.
417 if (!assetTag.size())
418 {
419 responseData->tagLength = 0;
420 memcpy(response, outPayload.data(), outPayload.size());
421 *data_len = outPayload.size();
422 return IPMI_CC_OK;
423 }
424
425 // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit
426 // Get Asset Tag command.
427 if (assetTag.size() > dcmi::assetTagMaxSize)
428 {
429 assetTag.resize(dcmi::assetTagMaxSize);
430 }
431
432 // If the requested offset is beyond the asset tag size.
433 if (requestData->offset >= assetTag.size())
434 {
435 *data_len = 0;
436 return IPMI_CC_PARM_OUT_OF_RANGE;
437 }
438
439 auto returnData = assetTag.substr(requestData->offset, requestData->bytes);
440
441 responseData->tagLength = assetTag.size();
442
443 memcpy(response, outPayload.data(), outPayload.size());
444 memcpy(static_cast<uint8_t*>(response) + outPayload.size(),
445 returnData.data(), returnData.size());
446 *data_len = outPayload.size() + returnData.size();
447
448 return IPMI_CC_OK;
449}
450
Tom Joseph545dd232017-07-12 20:20:49 +0530451ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
452 ipmi_request_t request, ipmi_response_t response,
453 ipmi_data_len_t data_len, ipmi_context_t context)
454{
455 auto requestData = reinterpret_cast<const dcmi::SetAssetTagRequest*>
456 (request);
457 std::vector<uint8_t> outPayload(sizeof(dcmi::SetAssetTagResponse));
458 auto responseData = reinterpret_cast<dcmi::SetAssetTagResponse*>
459 (outPayload.data());
460
461 if (requestData->groupID != dcmi::groupExtId)
462 {
463 *data_len = 0;
464 return IPMI_CC_INVALID_FIELD_REQUEST;
465 }
466
467 // Verify offset to read and number of bytes to read are not exceeding the
468 // range.
469 if ((requestData->offset > dcmi::assetTagMaxOffset) ||
470 (requestData->bytes > dcmi::maxBytes) ||
471 ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
472 {
473 *data_len = 0;
474 return IPMI_CC_PARM_OUT_OF_RANGE;
475 }
476
477 std::string assetTag;
478
479 try
480 {
481 assetTag = dcmi::readAssetTag();
482
483 if (requestData->offset > assetTag.size())
484 {
485 *data_len = 0;
486 return IPMI_CC_PARM_OUT_OF_RANGE;
487 }
488
489 assetTag.replace(requestData->offset,
490 assetTag.size() - requestData->offset,
491 static_cast<const char*>(request) +
492 sizeof(dcmi::SetAssetTagRequest),
493 requestData->bytes);
494
495 dcmi::writeAssetTag(assetTag);
496
497 responseData->groupID = dcmi::groupExtId;
498 responseData->tagLength = assetTag.size();
499 memcpy(response, outPayload.data(), outPayload.size());
500 *data_len = outPayload.size();
501
502 return IPMI_CC_OK;
503 }
504 catch (InternalFailure& e)
505 {
506 *data_len = 0;
507 return IPMI_CC_UNSPECIFIED_ERROR;
508 }
509}
510
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +0300511ipmi_ret_t getMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
512 ipmi_request_t request, ipmi_response_t response,
513 ipmi_data_len_t data_len, ipmi_context_t context)
514{
515 auto requestData = reinterpret_cast<const dcmi::GetMgmntCtrlIdStrRequest *>
516 (request);
517 auto responseData = reinterpret_cast<dcmi::GetMgmntCtrlIdStrResponse *>
518 (response);
519 std::string hostName;
520
521 *data_len = 0;
522
523 if (requestData->groupID != dcmi::groupExtId ||
524 requestData->bytes > dcmi::maxBytes ||
525 requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen)
526 {
527 return IPMI_CC_INVALID_FIELD_REQUEST;
528 }
529
530 try
531 {
532 hostName = dcmi::getHostName();
533 }
534 catch (InternalFailure& e)
535 {
536 return IPMI_CC_UNSPECIFIED_ERROR;
537 }
538
539 if (requestData->offset > hostName.length())
540 {
541 return IPMI_CC_PARM_OUT_OF_RANGE;
542 }
543 auto responseStr = hostName.substr(requestData->offset, requestData->bytes);
544 auto responseStrLen = std::min(static_cast<std::size_t>(requestData->bytes),
545 responseStr.length() + 1);
546 responseData->groupID = dcmi::groupExtId;
547 responseData->strLen = hostName.length();
548 std::copy(begin(responseStr), end(responseStr), responseData->data);
549
550 *data_len = sizeof(*responseData) + responseStrLen;
551 return IPMI_CC_OK;
552}
553
554ipmi_ret_t setMgmntCtrlIdStr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
555 ipmi_request_t request, ipmi_response_t response,
556 ipmi_data_len_t data_len, ipmi_context_t context)
557{
558 static std::array<char, dcmi::maxCtrlIdStrLen + 1> newCtrlIdStr;
559
560 auto requestData = reinterpret_cast<const dcmi::SetMgmntCtrlIdStrRequest *>
561 (request);
562 auto responseData = reinterpret_cast<dcmi::SetMgmntCtrlIdStrResponse *>
563 (response);
564
565 *data_len = 0;
566
567 if (requestData->groupID != dcmi::groupExtId ||
568 requestData->bytes > dcmi::maxBytes ||
569 requestData->offset + requestData->bytes > dcmi::maxCtrlIdStrLen + 1 ||
570 (requestData->offset + requestData->bytes == dcmi::maxCtrlIdStrLen + 1 &&
571 requestData->data[requestData->bytes - 1] != '\0'))
572 {
573 return IPMI_CC_INVALID_FIELD_REQUEST;
574 }
575
576 try
577 {
578 /* if there is no old value and offset is not 0 */
579 if (newCtrlIdStr[0] == '\0' && requestData->offset != 0)
580 {
581 /* read old ctrlIdStr */
582 auto hostName = dcmi::getHostName();
583 hostName.resize(dcmi::maxCtrlIdStrLen);
584 std::copy(begin(hostName), end(hostName), begin(newCtrlIdStr));
585 newCtrlIdStr[hostName.length()] = '\0';
586 }
587
588 /* replace part of string and mark byte after the last as \0 */
589 auto restStrIter = std::copy_n(requestData->data,
590 requestData->bytes, begin(newCtrlIdStr) + requestData->offset);
591 /* if the last written byte is not 64th - add '\0' */
592 if (requestData->offset + requestData->bytes <= dcmi::maxCtrlIdStrLen)
593 {
594 *restStrIter = '\0';
595 }
596
597 /* if input data contains '\0' whole string is sent - update hostname */
598 auto it = std::find(requestData->data,
599 requestData->data + requestData->bytes, '\0');
600 if (it != requestData->data + requestData->bytes)
601 {
602 sdbusplus::bus::bus bus{ ipmid_get_sd_bus_connection() };
603 ipmi::setDbusProperty(bus, dcmi::networkServiceName,
604 dcmi::networkConfigObj, dcmi::networkConfigIntf,
605 dcmi::hostNameProp, std::string(newCtrlIdStr.data()));
606 }
607 }
608 catch (InternalFailure& e)
609 {
610 *data_len = 0;
611 return IPMI_CC_UNSPECIFIED_ERROR;
612 }
613
614 responseData->groupID = dcmi::groupExtId;
615 responseData->offset = requestData->offset + requestData->bytes;
616 *data_len = sizeof(*responseData);
617 return IPMI_CC_OK;
618}
619
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -0600620//List of the capabilities under each parameter
621dcmi::DCMICaps dcmiCaps =
622{
623//Supported DCMI Capabilities
624 {
625 dcmi::DCMICapParameters::SUPPORTED_DCMI_CAPS,
626 {
627 3, {{"PowerManagement", 2, 0, 1},
628 {"OOBSecondaryLan", 3, 2, 1},
629 {"SerialTMODE", 3, 1, 1},
630 {"InBandSystemInterfaceChannel", 3, 0, 1}
631 }
632 }
633 },
634//Mandatory Platform Attributes
635 {
636 dcmi::DCMICapParameters::MANDATORY_PLAT_ATTRIBUTES,
637 {
638 5, {{"SELAutoRollOver", 1, 15, 1},
639 {"FlushEntireSELUponRollOver", 1, 14, 1},
640 {"RecordLevelSELFlushUponRollOver", 1, 13, 1},
641 {"NumberOfSELEntries", 1, 0, 12},
642 {"TempMonitoringSamplingFreq", 5, 0, 8}
643 }
644 }
645 },
646//Optional Platform Attributes
647 {
648 dcmi::DCMICapParameters::OPTIONAL_PLAT_ATTRIBUTES,
649 {
650 2, {{"PowerMgmtDeviceSlaveAddress", 1, 1, 7},
651 {"BMCChannelNumber", 2, 4, 4},
652 {"DeviceRivision", 2, 0, 4}
653 }
654 }
655 },
656//Manageability Access Attributes
657 {
658 dcmi::DCMICapParameters::MANAGEABILITY_ACCESS_ATTRIBUTES,
659 {
660 3, {{"MandatoryPrimaryLanOOBSupport", 1, 0, 8},
661 {"OptionalSecondaryLanOOBSupport", 2, 0, 8},
662 {"OptionalSerialOOBMTMODECapability", 3, 0, 8}
663 }
664 }
665 }
666};
667
668ipmi_ret_t getDCMICapabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
669 ipmi_request_t request, ipmi_response_t response,
670 ipmi_data_len_t data_len, ipmi_context_t context)
671{
672
673 std::ifstream dcmiCapFile(DCMI_CAP_JSON_FILE);
674 if (!dcmiCapFile.is_open())
675 {
676 log<level::ERR>("DCMI Capabilities file not found");
677 return IPMI_CC_UNSPECIFIED_ERROR;
678 }
679
680 auto data = nlohmann::json::parse(dcmiCapFile, nullptr, false);
681 if (data.is_discarded())
682 {
683 log<level::ERR>("DCMI Capabilities JSON parser failure");
684 return IPMI_CC_UNSPECIFIED_ERROR;
685 }
686
687 auto requestData = reinterpret_cast<const dcmi::GetDCMICapRequest*>
688 (request);
689
690 //get list of capabilities in a parameter
691 auto caps =
692 dcmiCaps.find(static_cast<dcmi::DCMICapParameters>(requestData->param));
693 if (caps == dcmiCaps.end())
694 {
695 log<level::ERR>("Invalid input parameter");
696 return IPMI_CC_INVALID_FIELD_REQUEST;
697 }
698
699 if (requestData->groupID != dcmi::groupExtId)
700 {
701 *data_len = 0;
702 return IPMI_CC_INVALID_FIELD_REQUEST;
703 }
704
705 auto responseData = reinterpret_cast<dcmi::GetDCMICapResponse*>
706 (response);
707
708 //For each capabilities in a parameter fill the data from
709 //the json file based on the capability name.
710 for (auto cap : caps->second.capList)
711 {
712 //If the data is beyond first byte boundary, insert in a
713 //16bit pattern for example number of SEL entries are represented
714 //in 12bits.
715 if ((cap.length + cap.position) > 8)
716 {
717 //Read the value corresponding to capability name and assign to
718 //16bit bitset.
719 std::bitset<16> val(data.value(cap.name.c_str(), 0));
720 val <<= cap.position;
721 reinterpret_cast<uint16_t*>(responseData->data)[
722 (cap.bytePosition - 1) / sizeof(uint16_t)] |= val.to_ulong();
723 }
724 else
725 {
726 responseData->data[cap.bytePosition - 1] |=
727 data.value(cap.name.c_str(), 0) << cap.position;
728 }
729 }
730
731 responseData->groupID = dcmi::groupExtId;
732 responseData->major = DCMI_SPEC_MAJOR_VERSION;
733 responseData->minor = DCMI_SPEC_MINOR_VERSION;
734 responseData->paramRevision = DCMI_PARAMETER_REVISION;
735 *data_len = sizeof(*responseData) + caps->second.size;
736
737 return IPMI_CC_OK;
738}
739
Deepak Kodihalliee717d72018-01-24 04:53:09 -0600740namespace dcmi
741{
742namespace temp_readings
743{
744
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -0600745Json parseConfig()
746{
747 std::ifstream jsonFile(configFile);
748 if (!jsonFile.is_open())
749 {
750 log<level::ERR>("Temperature readings JSON file not found");
751 elog<InternalFailure>();
752 }
753
754 auto data = Json::parse(jsonFile, nullptr, false);
755 if (data.is_discarded())
756 {
757 log<level::ERR>("Temperature readings JSON parser failure");
758 elog<InternalFailure>();
759 }
760
761 return data;
762}
763
764Temperature readTemp(const std::string& dbusService,
765 const std::string& dbusPath)
766{
767 // Read the temperature value from d-bus object. Need some conversion.
768 // As per the interface xyz.openbmc_project.Sensor.Value, the temperature
769 // is an int64_t and in degrees C. It needs to be scaled by using the
770 // formula Value * 10^Scale. The ipmi spec has the temperature as a uint8_t,
771 // with a separate single bit for the sign.
772
773 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
774 auto result = ipmi::getAllDbusProperties(bus, dbusService, dbusPath,
775 "xyz.openbmc_project.Sensor.Value");
776 auto temperature = result.at("Value").get<int64_t>();
777 uint64_t absTemp = std::abs(temperature);
778
779 auto factor = result.at("Scale").get<int64_t>();
780 uint64_t scale = std::pow(10, factor); // pow() returns float/double
781 unsigned long long tempDegrees = 0;
782 // Overflow safe multiplication when the scale is > 0
783 if (scale && __builtin_umulll_overflow(
784 absTemp, scale, &tempDegrees))
785 {
786 log<level::ERR>("Multiplication overflow detected",
787 entry("TEMP_VALUE=%llu", absTemp),
788 entry("SCALE_FACTOR=%llu", scale));
789 elog<InternalFailure>();
790 }
791 else
792 {
793 // The (uint64_t)scale value is 0, effectively this is division
794 tempDegrees = absTemp * std::pow(10, factor);
795 }
796 // Max absolute temp as per ipmi spec is 128.
797 if (tempDegrees > maxTemp)
798 {
799 tempDegrees = maxTemp;
800 }
801
802 return std::make_tuple(static_cast<uint8_t>(tempDegrees),
803 (temperature < 0));
804}
805
Deepak Kodihalliee717d72018-01-24 04:53:09 -0600806std::tuple<Response, NumInstances> read(const std::string& type,
807 uint8_t instance)
808{
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -0600809 Response response{};
810 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
811
812 if (!instance)
813 {
814 log<level::ERR>("Expected non-zero instance");
815 elog<InternalFailure>();
816 }
817
818 auto data = parseConfig();
819 static const std::vector<Json> empty{};
820 std::vector<Json> readings = data.value(type, empty);
821 size_t numInstances = readings.size();
822 for (const auto& j : readings)
823 {
824 uint8_t instanceNum = j.value("instance", 0);
825 // Not the instance we're interested in
826 if (instanceNum != instance)
827 {
828 continue;
829 }
830
831 std::string path = j.value("dbus", "");
832 std::string service;
833 try
834 {
835 service =
836 ipmi::getService(bus,
837 "xyz.openbmc_project.Sensor.Value",
838 path);
839 }
840 catch (std::exception& e)
841 {
842 log<level::DEBUG>(e.what());
843 return std::make_tuple(response, numInstances);
844 }
845
846 response.instance = instance;
847 uint8_t temp{};
848 bool sign{};
849 std::tie(temp, sign) = readTemp(service, path);
850 response.temperature = temp;
851 response.sign = sign;
852
853 // Found the instance we're interested in
854 break;
855 }
856
857 if (numInstances > maxInstances)
858 {
859 numInstances = maxInstances;
860 }
861 return std::make_tuple(response, numInstances);
Deepak Kodihalliee717d72018-01-24 04:53:09 -0600862}
863
864std::tuple<ResponseList, NumInstances> readAll(const std::string& type,
865 uint8_t instanceStart)
866{
Deepak Kodihallib1e8fba2018-01-24 04:57:10 -0600867 ResponseList response{};
868 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
869
870 size_t numInstances = 0;
871 auto data = parseConfig();
872 static const std::vector<Json> empty{};
873 std::vector<Json> readings = data.value(type, empty);
874 numInstances = readings.size();
875 for (const auto& j : readings)
876 {
877 try
878 {
879 // Max of 8 response data sets
880 if (response.size() == maxDataSets)
881 {
882 break;
883 }
884
885 uint8_t instanceNum = j.value("instance", 0);
886 // Not in the instance range we're interested in
887 if (instanceNum < instanceStart)
888 {
889 continue;
890 }
891
892 std::string path = j.value("dbus", "");
893 auto service =
894 ipmi::getService(bus,
895 "xyz.openbmc_project.Sensor.Value",
896 path);
897
898 Response r{};
899 r.instance = instanceNum;
900 uint8_t temp{};
901 bool sign{};
902 std::tie(temp, sign) = readTemp(service, path);
903 r.temperature = temp;
904 r.sign = sign;
905 response.push_back(r);
906 }
907 catch (std::exception& e)
908 {
909 log<level::DEBUG>(e.what());
910 continue;
911 }
912 }
913
914 if (numInstances > maxInstances)
915 {
916 numInstances = maxInstances;
917 }
918 return std::make_tuple(response, numInstances);
Deepak Kodihalliee717d72018-01-24 04:53:09 -0600919}
920
921} // namsespace temp_readings
922} // namsepace dcmi
923
924ipmi_ret_t getTempReadings(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
925 ipmi_request_t request, ipmi_response_t response,
926 ipmi_data_len_t data_len, ipmi_context_t context)
927{
928 // Refer Table 6-14, DCMI Entity ID Extension, DCMI v1.5 spec
929 static const std::map<uint8_t, std::string> entityIdToName
930 {
931 {0x40, "inlet"},
932 {0x37, "inlet"},
933 {0x41, "cpu"},
934 {0x03, "cpu"},
935 {0x42, "baseboard"},
936 {0x07, "baseboard"}
937 };
938
939 auto requestData =
940 reinterpret_cast<const dcmi::GetTempReadingsRequest*>(request);
941 auto responseData =
942 reinterpret_cast<dcmi::GetTempReadingsResponseHdr*>(response);
943
944 if (*data_len != sizeof(dcmi::GetTempReadingsRequest))
945 {
946 log<level::ERR>("Malformed request data",
947 entry("DATA_SIZE=%d", *data_len));
948 return IPMI_CC_REQ_DATA_LEN_INVALID;
949 }
950 *data_len = 0;
951
952 auto it = entityIdToName.find(requestData->entityId);
953 if (it == entityIdToName.end())
954 {
955 log<level::ERR>("Unknown Entity ID",
956 entry("ENTITY_ID=%d", requestData->entityId));
957 return IPMI_CC_INVALID_FIELD_REQUEST;
958 }
959
960 if (requestData->groupID != dcmi::groupExtId)
961 {
962 log<level::ERR>("Invalid Group ID",
963 entry("GROUP_ID=%d", requestData->groupID));
964 return IPMI_CC_INVALID_FIELD_REQUEST;
965 }
966
967 if (requestData->sensorType != dcmi::temperatureSensorType)
968 {
969 log<level::ERR>("Invalid sensor type",
970 entry("SENSOR_TYPE=%d", requestData->sensorType));
971 return IPMI_CC_INVALID_FIELD_REQUEST;
972 }
973
974 dcmi::temp_readings::ResponseList temps{};
975 try
976 {
977 if (!requestData->entityInstance)
978 {
979 // Read all instances
980 std::tie(temps, responseData->numInstances) =
981 dcmi::temp_readings::readAll(it->second,
982 requestData->instanceStart);
983 }
984 else
985 {
986 // Read one instance
987 temps.resize(1);
988 std::tie(temps[0], responseData->numInstances) =
989 dcmi::temp_readings::read(it->second,
990 requestData->entityInstance);
991 }
992 responseData->numDataSets = temps.size();
993 }
994 catch (InternalFailure& e)
995 {
996 return IPMI_CC_UNSPECIFIED_ERROR;
997 }
998
999 responseData->groupID = dcmi::groupExtId;
1000 size_t payloadSize =
1001 temps.size() * sizeof(dcmi::temp_readings::Response);
1002 if (!temps.empty())
1003 {
1004 memcpy(responseData + 1, // copy payload right after the response header
1005 temps.data(),
1006 payloadSize);
1007 }
1008 *data_len = sizeof(dcmi::GetTempReadingsResponseHdr) + payloadSize;
1009
1010 return IPMI_CC_OK;
1011}
1012
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001013int64_t getPowerReading(sdbusplus::bus::bus& bus)
1014{
1015 std::ifstream sensorFile(POWER_READING_SENSOR);
1016 std::string objectPath;
1017 if (!sensorFile.is_open())
1018 {
1019 log<level::ERR>("Power reading configuration file not found",
1020 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1021 elog<InternalFailure>();
1022 }
1023
1024 auto data = nlohmann::json::parse(sensorFile, nullptr, false);
1025 if (data.is_discarded())
1026 {
1027 log<level::ERR>("Error in parsing configuration file",
1028 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1029 elog<InternalFailure>();
1030 }
1031
1032 objectPath = data.value("path", "");
1033 if (objectPath.empty())
1034 {
1035 log<level::ERR>("Power sensor D-Bus object path is empty",
1036 entry("POWER_SENSOR_FILE=%s", POWER_READING_SENSOR));
1037 elog<InternalFailure>();
1038 }
1039
1040 auto service = ipmi::getService(bus, SENSOR_VALUE_INTF, objectPath);
1041
1042 //Read the sensor value and scale properties
1043 auto properties = ipmi::getAllDbusProperties(
1044 bus, service, objectPath, SENSOR_VALUE_INTF);
1045 auto power = properties[SENSOR_VALUE_PROP].get<int64_t>();
1046 auto scale = properties[SENSOR_SCALE_PROP].get<int64_t>();
1047
1048 // Power reading needs to be scaled with the Scale value using the formula
1049 // Value * 10^Scale.
1050 power *= std::pow(10, scale);
1051
1052 return power;
1053}
1054
1055ipmi_ret_t getPowerReading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
1056 ipmi_request_t request, ipmi_response_t response,
1057 ipmi_data_len_t data_len, ipmi_context_t context)
1058{
1059 ipmi_ret_t rc = IPMI_CC_OK;
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001060 auto requestData = reinterpret_cast<const dcmi::GetPowerReadingRequest*>
1061 (request);
1062 auto responseData = reinterpret_cast<dcmi::GetPowerReadingResponse*>
1063 (response);
1064
1065 if (requestData->groupID != dcmi::groupExtId)
1066 {
1067 *data_len = 0;
1068 return IPMI_CC_INVALID_FIELD_REQUEST;
1069 }
1070
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001071 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001072 int64_t power = 0;
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001073 try
1074 {
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001075 power = getPowerReading(bus);
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001076 }
1077 catch (InternalFailure& e)
1078 {
1079 log<level::ERR>("Error in reading power sensor value",
1080 entry("INTERFACE=%s", SENSOR_VALUE_INTF),
1081 entry("PROPERTY=%s", SENSOR_VALUE_PROP));
1082 return IPMI_CC_UNSPECIFIED_ERROR;
1083 }
Marri Devender Rao9c966e02018-01-22 05:55:10 -06001084 responseData->groupID = dcmi::groupExtId;
1085
1086 // TODO: openbmc/openbmc#2819
1087 // Minumum, Maximum, Average power, TimeFrame, TimeStamp,
1088 // PowerReadingState readings need to be populated
1089 // after Telemetry changes.
1090 uint16_t totalPower = static_cast<uint16_t>(power);
1091 responseData->currentPower = totalPower;
1092 responseData->minimumPower = totalPower;
1093 responseData->maximumPower = totalPower;
1094 responseData->averagePower = totalPower;
1095
1096 *data_len = sizeof(*responseData);
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001097 return rc;
1098}
1099
Chris Austen1810bec2015-10-13 12:12:39 -05001100void register_netfn_dcmi_functions()
1101{
Tom05732372016-09-06 17:21:23 +05301102 // <Get Power Limit>
Ratan Gupta11ddbd22017-08-05 11:59:39 +05301103 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1104 NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT);
1105
1106 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT,
1107 NULL, getPowerLimit, PRIVILEGE_USER);
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301108
Tom Joseph46fa37d2017-07-26 18:11:55 +05301109 // <Set Power Limit>
Ratan Gupta11ddbd22017-08-05 11:59:39 +05301110 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1111 NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT);
1112
1113 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT,
1114 NULL, setPowerLimit, PRIVILEGE_OPERATOR);
Tom Joseph46fa37d2017-07-26 18:11:55 +05301115
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301116 // <Activate/Deactivate Power Limit>
Ratan Gupta11ddbd22017-08-05 11:59:39 +05301117 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1118 NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT);
1119
1120 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT,
1121 NULL, applyPowerLimit, PRIVILEGE_OPERATOR);
Tom Joseph6c8d51b2017-07-26 18:18:06 +05301122
Tom Joseph6f6dd4d2017-07-12 20:07:11 +05301123 // <Get Asset Tag>
Ratan Gupta11ddbd22017-08-05 11:59:39 +05301124 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1125 NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG);
1126
1127 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG,
1128 NULL, getAssetTag, PRIVILEGE_USER);
Tom Joseph545dd232017-07-12 20:20:49 +05301129
1130 // <Set Asset Tag>
Ratan Gupta11ddbd22017-08-05 11:59:39 +05301131 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1132 NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG);
1133
1134 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG,
1135 NULL, setAssetTag, PRIVILEGE_OPERATOR);
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001136
Gunnar Mills8991dd62017-10-25 17:11:29 -05001137 // <Get Management Controller Identifier String>
Vladislav Vovchenko8f7a6f62017-08-17 00:31:14 +03001138 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1139 NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR);
1140
1141 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_MGMNT_CTRL_ID_STR,
1142 NULL, getMgmntCtrlIdStr, PRIVILEGE_USER);
1143
1144 // <Set Management Controller Identifier String>
1145 printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
1146 NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR);
1147 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_MGMNT_CTRL_ID_STR,
1148 NULL, setMgmntCtrlIdStr, PRIVILEGE_ADMIN);
1149
Dhruvaraj Subhashchandrane29be412018-01-16 05:11:56 -06001150 // <Get DCMI capabilities>
1151 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_CAPABILITIES,
1152 NULL, getDCMICapabilities, PRIVILEGE_USER);
Deepak Kodihalliee717d72018-01-24 04:53:09 -06001153
1154 // <Get Temperature Readings>
1155 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_TEMP_READINGS,
1156 NULL, getTempReadings, PRIVILEGE_USER);
1157
Marri Devender Rao66c5fda2018-01-18 10:48:37 -06001158 // <Get Power Reading>
1159 ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_READING,
1160 NULL, getPowerReading, PRIVILEGE_USER);
Chris Austen1810bec2015-10-13 12:12:39 -05001161 return;
1162}
Tom05732372016-09-06 17:21:23 +05301163// 956379