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