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