1 #include "dcmihandler.hpp"
2 #include "host-ipmid/ipmid-api.h"
3 #include <phosphor-logging/elog-errors.hpp>
4 #include <phosphor-logging/log.hpp>
5 #include <sdbusplus/bus.hpp>
6 #include "utils.hpp"
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdint.h>
10 #include "xyz/openbmc_project/Common/error.hpp"
11 
12 using namespace phosphor::logging;
13 using InternalFailure =
14         sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
15 
16 void register_netfn_dcmi_functions() __attribute__((constructor));
17 
18 constexpr auto PCAP_PATH    = "/xyz/openbmc_project/control/host0/power_cap";
19 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
20 
21 constexpr auto POWER_CAP_PROP = "PowerCap";
22 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
23 
24 using namespace phosphor::logging;
25 
26 namespace dcmi
27 {
28 
29 uint32_t getPcap(sdbusplus::bus::bus& bus)
30 {
31     auto settingService = ipmi::getService(bus,
32                                            PCAP_INTERFACE,PCAP_PATH);
33 
34     auto method = bus.new_method_call(settingService.c_str(),
35                                       PCAP_PATH,
36                                       "org.freedesktop.DBus.Properties",
37                                       "Get");
38 
39     method.append(PCAP_INTERFACE, POWER_CAP_PROP);
40     auto reply = bus.call(method);
41 
42     if (reply.is_method_error())
43     {
44         log<level::ERR>("Error in getPcap prop");
45         elog<InternalFailure>();
46     }
47     sdbusplus::message::variant<uint32_t> pcap;
48     reply.read(pcap);
49 
50     return pcap.get<uint32_t>();
51 }
52 
53 bool getPcapEnabled(sdbusplus::bus::bus& bus)
54 {
55     auto settingService = ipmi::getService(bus,
56                                            PCAP_INTERFACE,PCAP_PATH);
57 
58     auto method = bus.new_method_call(settingService.c_str(),
59                                       PCAP_PATH,
60                                       "org.freedesktop.DBus.Properties",
61                                       "Get");
62 
63     method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
64     auto reply = bus.call(method);
65 
66     if (reply.is_method_error())
67     {
68         log<level::ERR>("Error in getPcapEnabled prop");
69         elog<InternalFailure>();
70     }
71     sdbusplus::message::variant<bool> pcapEnabled;
72     reply.read(pcapEnabled);
73 
74     return pcapEnabled.get<bool>();
75 }
76 
77 void setPcap(sdbusplus::bus::bus& bus, const uint32_t powerCap)
78 {
79     auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
80 
81     auto method = bus.new_method_call(service.c_str(),
82                                       PCAP_PATH,
83                                       "org.freedesktop.DBus.Properties",
84                                       "Set");
85 
86     method.append(PCAP_INTERFACE, POWER_CAP_PROP);
87     method.append(sdbusplus::message::variant<uint32_t>(powerCap));
88 
89     auto reply = bus.call(method);
90 
91     if (reply.is_method_error())
92     {
93         log<level::ERR>("Error in setPcap property");
94         elog<InternalFailure>();
95     }
96 }
97 
98 void setPcapEnable(sdbusplus::bus::bus& bus, bool enabled)
99 {
100     auto service = ipmi::getService(bus, PCAP_INTERFACE, PCAP_PATH);
101 
102     auto method = bus.new_method_call(service.c_str(),
103                                       PCAP_PATH,
104                                       "org.freedesktop.DBus.Properties",
105                                       "Set");
106 
107     method.append(PCAP_INTERFACE, POWER_CAP_ENABLE_PROP);
108     method.append(sdbusplus::message::variant<bool>(enabled));
109 
110     auto reply = bus.call(method);
111 
112     if (reply.is_method_error())
113     {
114         log<level::ERR>("Error in setPcapEnabled property");
115         elog<InternalFailure>();
116     }
117 }
118 
119 void readAssetTagObjectTree(dcmi::assettag::ObjectTree& objectTree)
120 {
121     static constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper";
122     static constexpr auto mapperObjPath = "/xyz/openbmc_project/object_mapper";
123     static constexpr auto mapperIface = "xyz.openbmc_project.ObjectMapper";
124     static constexpr auto inventoryRoot = "/xyz/openbmc_project/inventory/";
125 
126     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
127     auto depth = 0;
128 
129     auto mapperCall = bus.new_method_call(mapperBusName,
130                                           mapperObjPath,
131                                           mapperIface,
132                                           "GetSubTree");
133 
134     mapperCall.append(inventoryRoot);
135     mapperCall.append(depth);
136     mapperCall.append(std::vector<std::string>({dcmi::assetTagIntf}));
137 
138     auto mapperReply = bus.call(mapperCall);
139     if (mapperReply.is_method_error())
140     {
141         log<level::ERR>("Error in mapper call");
142         elog<InternalFailure>();
143     }
144 
145     mapperReply.read(objectTree);
146 
147     if (objectTree.empty())
148     {
149         log<level::ERR>("AssetTag property is not populated");
150         elog<InternalFailure>();
151     }
152 }
153 
154 std::string readAssetTag()
155 {
156     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
157     dcmi::assettag::ObjectTree objectTree;
158 
159     // Read the object tree with the inventory root to figure out the object
160     // that has implemented the Asset tag interface.
161     readAssetTagObjectTree(objectTree);
162 
163     auto method = bus.new_method_call(
164             (objectTree.begin()->second.begin()->first).c_str(),
165             (objectTree.begin()->first).c_str(),
166             dcmi::propIntf,
167             "Get");
168     method.append(dcmi::assetTagIntf);
169     method.append(dcmi::assetTagProp);
170 
171     auto reply = bus.call(method);
172     if (reply.is_method_error())
173     {
174         log<level::ERR>("Error in reading asset tag");
175         elog<InternalFailure>();
176     }
177 
178     sdbusplus::message::variant<std::string> assetTag;
179     reply.read(assetTag);
180 
181     return assetTag.get<std::string>();
182 }
183 
184 void writeAssetTag(const std::string& assetTag)
185 {
186     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
187     dcmi::assettag::ObjectTree objectTree;
188 
189     // Read the object tree with the inventory root to figure out the object
190     // that has implemented the Asset tag interface.
191     readAssetTagObjectTree(objectTree);
192 
193     auto method = bus.new_method_call(
194             (objectTree.begin()->second.begin()->first).c_str(),
195             (objectTree.begin()->first).c_str(),
196             dcmi::propIntf,
197             "Set");
198     method.append(dcmi::assetTagIntf);
199     method.append(dcmi::assetTagProp);
200     method.append(sdbusplus::message::variant<std::string>(assetTag));
201 
202     auto reply = bus.call(method);
203     if (reply.is_method_error())
204     {
205         log<level::ERR>("Error in writing asset tag");
206         elog<InternalFailure>();
207     }
208 }
209 
210 } // namespace dcmi
211 
212 ipmi_ret_t getPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
213                          ipmi_request_t request, ipmi_response_t response,
214                          ipmi_data_len_t data_len, ipmi_context_t context)
215 {
216     auto requestData = reinterpret_cast<const dcmi::GetPowerLimitRequest*>
217                    (request);
218     std::vector<uint8_t> outPayload(sizeof(dcmi::GetPowerLimitResponse));
219     auto responseData = reinterpret_cast<dcmi::GetPowerLimitResponse*>
220             (outPayload.data());
221 
222     if (requestData->groupID != dcmi::groupExtId)
223     {
224         *data_len = 0;
225         return IPMI_CC_INVALID_FIELD_REQUEST;
226     }
227 
228     sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
229     uint32_t pcapValue = 0;
230     bool pcapEnable = false;
231 
232     try
233     {
234         pcapValue = dcmi::getPcap(sdbus);
235         pcapEnable = dcmi::getPcapEnabled(sdbus);
236     }
237     catch (InternalFailure& e)
238     {
239         *data_len = 0;
240         return IPMI_CC_UNSPECIFIED_ERROR;
241     }
242 
243     responseData->groupID = dcmi::groupExtId;
244 
245     /*
246      * Exception action if power limit is exceeded and cannot be controlled
247      * with the correction time limit is hardcoded to Hard Power Off system
248      * and log event to SEL.
249      */
250     constexpr auto exception = 0x01;
251     responseData->exceptionAction = exception;
252 
253     responseData->powerLimit = static_cast<uint16_t>(pcapValue);
254 
255     /*
256      * Correction time limit and Statistics sampling period is currently not
257      * populated.
258      */
259 
260     *data_len = outPayload.size();
261     memcpy(response, outPayload.data(), *data_len);
262 
263     if (pcapEnable)
264     {
265         return IPMI_CC_OK;
266     }
267     else
268     {
269         return IPMI_DCMI_CC_NO_ACTIVE_POWER_LIMIT;
270     }
271 }
272 
273 ipmi_ret_t setPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
274                          ipmi_request_t request, ipmi_response_t response,
275                          ipmi_data_len_t data_len, ipmi_context_t context)
276 {
277     auto requestData = reinterpret_cast<const dcmi::SetPowerLimitRequest*>
278                    (request);
279     std::vector<uint8_t> outPayload(sizeof(dcmi::SetPowerLimitResponse));
280     auto responseData = reinterpret_cast<dcmi::SetPowerLimitResponse*>
281             (outPayload.data());
282 
283     if (requestData->groupID != dcmi::groupExtId)
284     {
285         *data_len = 0;
286         return IPMI_CC_INVALID_FIELD_REQUEST;
287     }
288 
289     sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
290 
291     // Only process the power limit requested in watts.
292     try
293     {
294         dcmi::setPcap(sdbus, requestData->powerLimit);
295     }
296     catch (InternalFailure& e)
297     {
298         *data_len = 0;
299         return IPMI_CC_UNSPECIFIED_ERROR;
300     }
301 
302     log<level::INFO>("Set Power Cap",
303                      entry("POWERCAP=%u", requestData->powerLimit));
304 
305     responseData->groupID = dcmi::groupExtId;
306     memcpy(response, outPayload.data(), outPayload.size());
307     *data_len = outPayload.size();
308 
309     return IPMI_CC_OK;
310 }
311 
312 ipmi_ret_t applyPowerLimit(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
313                            ipmi_request_t request, ipmi_response_t response,
314                            ipmi_data_len_t data_len, ipmi_context_t context)
315 {
316     auto requestData = reinterpret_cast<const dcmi::ApplyPowerLimitRequest*>
317                    (request);
318     std::vector<uint8_t> outPayload(sizeof(dcmi::ApplyPowerLimitResponse));
319     auto responseData = reinterpret_cast<dcmi::ApplyPowerLimitResponse*>
320             (outPayload.data());
321 
322     if (requestData->groupID != dcmi::groupExtId)
323     {
324         *data_len = 0;
325         return IPMI_CC_INVALID_FIELD_REQUEST;
326     }
327 
328     sdbusplus::bus::bus sdbus {ipmid_get_sd_bus_connection()};
329 
330     try
331     {
332         dcmi::setPcapEnable(sdbus,
333                             static_cast<bool>(requestData->powerLimitAction));
334     }
335     catch (InternalFailure& e)
336     {
337         *data_len = 0;
338         return IPMI_CC_UNSPECIFIED_ERROR;
339     }
340 
341     log<level::INFO>("Set Power Cap Enable",
342                      entry("POWERCAPENABLE=%u", requestData->powerLimitAction));
343 
344     responseData->groupID = dcmi::groupExtId;
345     memcpy(response, outPayload.data(), outPayload.size());
346     *data_len = outPayload.size();
347 
348     return IPMI_CC_OK;
349 }
350 
351 ipmi_ret_t getAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
352                        ipmi_request_t request, ipmi_response_t response,
353                        ipmi_data_len_t data_len, ipmi_context_t context)
354 {
355     auto requestData = reinterpret_cast<const dcmi::GetAssetTagRequest*>
356                    (request);
357     std::vector<uint8_t> outPayload(sizeof(dcmi::GetAssetTagResponse));
358     auto responseData = reinterpret_cast<dcmi::GetAssetTagResponse*>
359             (outPayload.data());
360 
361     if (requestData->groupID != dcmi::groupExtId)
362     {
363         *data_len = 0;
364         return IPMI_CC_INVALID_FIELD_REQUEST;
365     }
366 
367     // Verify offset to read and number of bytes to read are not exceeding the
368     // range.
369     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
370         (requestData->bytes > dcmi::maxBytes) ||
371         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
372     {
373         *data_len = 0;
374         return IPMI_CC_PARM_OUT_OF_RANGE;
375     }
376 
377     std::string assetTag;
378 
379     try
380     {
381         assetTag = dcmi::readAssetTag();
382     }
383     catch (InternalFailure& e)
384     {
385         *data_len = 0;
386         return IPMI_CC_UNSPECIFIED_ERROR;
387     }
388 
389     responseData->groupID = dcmi::groupExtId;
390 
391     // Return if the asset tag is not populated.
392     if (!assetTag.size())
393     {
394         responseData->tagLength = 0;
395         memcpy(response, outPayload.data(), outPayload.size());
396         *data_len = outPayload.size();
397         return IPMI_CC_OK;
398     }
399 
400     // If the asset tag is longer than 63 bytes, restrict it to 63 bytes to suit
401     // Get Asset Tag command.
402     if (assetTag.size() > dcmi::assetTagMaxSize)
403     {
404         assetTag.resize(dcmi::assetTagMaxSize);
405     }
406 
407     // If the requested offset is beyond the asset tag size.
408     if (requestData->offset >= assetTag.size())
409     {
410         *data_len = 0;
411         return IPMI_CC_PARM_OUT_OF_RANGE;
412     }
413 
414     auto returnData = assetTag.substr(requestData->offset, requestData->bytes);
415 
416     responseData->tagLength = assetTag.size();
417 
418     memcpy(response, outPayload.data(), outPayload.size());
419     memcpy(static_cast<uint8_t*>(response) + outPayload.size(),
420            returnData.data(), returnData.size());
421     *data_len = outPayload.size() + returnData.size();
422 
423     return IPMI_CC_OK;
424 }
425 
426 ipmi_ret_t setAssetTag(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
427                        ipmi_request_t request, ipmi_response_t response,
428                        ipmi_data_len_t data_len, ipmi_context_t context)
429 {
430     auto requestData = reinterpret_cast<const dcmi::SetAssetTagRequest*>
431                    (request);
432     std::vector<uint8_t> outPayload(sizeof(dcmi::SetAssetTagResponse));
433     auto responseData = reinterpret_cast<dcmi::SetAssetTagResponse*>
434             (outPayload.data());
435 
436     if (requestData->groupID != dcmi::groupExtId)
437     {
438         *data_len = 0;
439         return IPMI_CC_INVALID_FIELD_REQUEST;
440     }
441 
442     // Verify offset to read and number of bytes to read are not exceeding the
443     // range.
444     if ((requestData->offset > dcmi::assetTagMaxOffset) ||
445         (requestData->bytes > dcmi::maxBytes) ||
446         ((requestData->offset + requestData->bytes) > dcmi::assetTagMaxSize))
447     {
448         *data_len = 0;
449         return IPMI_CC_PARM_OUT_OF_RANGE;
450     }
451 
452     std::string assetTag;
453 
454     try
455     {
456         assetTag = dcmi::readAssetTag();
457 
458         if (requestData->offset > assetTag.size())
459         {
460             *data_len = 0;
461             return IPMI_CC_PARM_OUT_OF_RANGE;
462         }
463 
464         assetTag.replace(requestData->offset,
465                          assetTag.size() - requestData->offset,
466                          static_cast<const char*>(request) +
467                          sizeof(dcmi::SetAssetTagRequest),
468                          requestData->bytes);
469 
470         dcmi::writeAssetTag(assetTag);
471 
472         responseData->groupID = dcmi::groupExtId;
473         responseData->tagLength = assetTag.size();
474         memcpy(response, outPayload.data(), outPayload.size());
475         *data_len = outPayload.size();
476 
477         return IPMI_CC_OK;
478     }
479     catch (InternalFailure& e)
480     {
481         *data_len = 0;
482         return IPMI_CC_UNSPECIFIED_ERROR;
483     }
484 }
485 
486 void register_netfn_dcmi_functions()
487 {
488     // <Get Power Limit>
489     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
490             NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT);
491 
492     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_POWER_LIMIT,
493                            NULL, getPowerLimit, PRIVILEGE_USER);
494 
495     // <Set Power Limit>
496     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
497             NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT);
498 
499     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_POWER_LIMIT,
500                            NULL, setPowerLimit, PRIVILEGE_OPERATOR);
501 
502     // <Activate/Deactivate Power Limit>
503     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
504             NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT);
505 
506     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::APPLY_POWER_LIMIT,
507                            NULL, applyPowerLimit, PRIVILEGE_OPERATOR);
508 
509     // <Get Asset Tag>
510     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
511             NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG);
512 
513     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::GET_ASSET_TAG,
514                            NULL, getAssetTag, PRIVILEGE_USER);
515 
516     // <Set Asset Tag>
517     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
518             NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG);
519 
520     ipmi_register_callback(NETFUN_GRPEXT, dcmi::Commands::SET_ASSET_TAG,
521                            NULL, setAssetTag, PRIVILEGE_OPERATOR);
522     return;
523 }
524 // 956379
525