1 #include "utils.hpp" 2 3 #include <libpldm/pdr.h> 4 #include <libpldm/pldm_types.h> 5 6 #include <phosphor-logging/lg2.hpp> 7 #include <xyz/openbmc_project/Common/error.hpp> 8 9 #include <algorithm> 10 #include <array> 11 #include <cctype> 12 #include <ctime> 13 #include <fstream> 14 #include <iostream> 15 #include <map> 16 #include <stdexcept> 17 #include <string> 18 #include <vector> 19 20 PHOSPHOR_LOG2_USING; 21 22 namespace pldm 23 { 24 namespace utils 25 { 26 constexpr auto mapperBusName = "xyz.openbmc_project.ObjectMapper"; 27 constexpr auto mapperPath = "/xyz/openbmc_project/object_mapper"; 28 constexpr auto mapperInterface = "xyz.openbmc_project.ObjectMapper"; 29 30 std::vector<std::vector<uint8_t>> findStateEffecterPDR(uint8_t /*tid*/, 31 uint16_t entityID, 32 uint16_t stateSetId, 33 const pldm_pdr* repo) 34 { 35 uint8_t* outData = nullptr; 36 uint32_t size{}; 37 const pldm_pdr_record* record{}; 38 std::vector<std::vector<uint8_t>> pdrs; 39 try 40 { 41 do 42 { 43 record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_EFFECTER_PDR, 44 record, &outData, &size); 45 if (record) 46 { 47 auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(outData); 48 auto compositeEffecterCount = pdr->composite_effecter_count; 49 auto possible_states_start = pdr->possible_states; 50 51 for (auto effecters = 0x00; effecters < compositeEffecterCount; 52 effecters++) 53 { 54 auto possibleStates = 55 reinterpret_cast<state_effecter_possible_states*>( 56 possible_states_start); 57 auto setId = possibleStates->state_set_id; 58 auto possibleStateSize = 59 possibleStates->possible_states_size; 60 61 if (pdr->entity_type == entityID && setId == stateSetId) 62 { 63 std::vector<uint8_t> effecter_pdr(&outData[0], 64 &outData[size]); 65 pdrs.emplace_back(std::move(effecter_pdr)); 66 break; 67 } 68 possible_states_start += possibleStateSize + sizeof(setId) + 69 sizeof(possibleStateSize); 70 } 71 } 72 73 } while (record); 74 } 75 catch (const std::exception& e) 76 { 77 error(" Failed to obtain a record. ERROR = {ERR_EXCEP}", "ERR_EXCEP", 78 e.what()); 79 } 80 81 return pdrs; 82 } 83 84 std::vector<std::vector<uint8_t>> findStateSensorPDR(uint8_t /*tid*/, 85 uint16_t entityID, 86 uint16_t stateSetId, 87 const pldm_pdr* repo) 88 { 89 uint8_t* outData = nullptr; 90 uint32_t size{}; 91 const pldm_pdr_record* record{}; 92 std::vector<std::vector<uint8_t>> pdrs; 93 try 94 { 95 do 96 { 97 record = pldm_pdr_find_record_by_type(repo, PLDM_STATE_SENSOR_PDR, 98 record, &outData, &size); 99 if (record) 100 { 101 auto pdr = reinterpret_cast<pldm_state_sensor_pdr*>(outData); 102 auto compositeSensorCount = pdr->composite_sensor_count; 103 auto possible_states_start = pdr->possible_states; 104 105 for (auto sensors = 0x00; sensors < compositeSensorCount; 106 sensors++) 107 { 108 auto possibleStates = 109 reinterpret_cast<state_sensor_possible_states*>( 110 possible_states_start); 111 auto setId = possibleStates->state_set_id; 112 auto possibleStateSize = 113 possibleStates->possible_states_size; 114 115 if (pdr->entity_type == entityID && setId == stateSetId) 116 { 117 std::vector<uint8_t> sensor_pdr(&outData[0], 118 &outData[size]); 119 pdrs.emplace_back(std::move(sensor_pdr)); 120 break; 121 } 122 possible_states_start += possibleStateSize + sizeof(setId) + 123 sizeof(possibleStateSize); 124 } 125 } 126 127 } while (record); 128 } 129 catch (const std::exception& e) 130 { 131 error(" Failed to obtain a record. ERROR = {ERR_EXCEP}", "ERR_EXCEP", 132 e.what()); 133 } 134 135 return pdrs; 136 } 137 138 uint8_t readHostEID() 139 { 140 uint8_t eid{}; 141 std::ifstream eidFile{HOST_EID_PATH}; 142 if (!eidFile.good()) 143 { 144 error("Could not open host EID file: {HOST_PATH}", "HOST_PATH", 145 static_cast<std::string>(HOST_EID_PATH)); 146 } 147 else 148 { 149 std::string eidStr; 150 eidFile >> eidStr; 151 if (!eidStr.empty()) 152 { 153 eid = atoi(eidStr.c_str()); 154 } 155 else 156 { 157 error("Host EID file was empty"); 158 } 159 } 160 161 return eid; 162 } 163 164 uint8_t getNumPadBytes(uint32_t data) 165 { 166 uint8_t pad; 167 pad = ((data % 4) ? (4 - data % 4) : 0); 168 return pad; 169 } // end getNumPadBytes 170 171 bool uintToDate(uint64_t data, uint16_t* year, uint8_t* month, uint8_t* day, 172 uint8_t* hour, uint8_t* min, uint8_t* sec) 173 { 174 constexpr uint64_t max_data = 29991231115959; 175 constexpr uint64_t min_data = 19700101000000; 176 if (data < min_data || data > max_data) 177 { 178 return false; 179 } 180 181 *year = data / 10000000000; 182 data = data % 10000000000; 183 *month = data / 100000000; 184 data = data % 100000000; 185 *day = data / 1000000; 186 data = data % 1000000; 187 *hour = data / 10000; 188 data = data % 10000; 189 *min = data / 100; 190 *sec = data % 100; 191 192 return true; 193 } 194 195 std::optional<std::vector<set_effecter_state_field>> 196 parseEffecterData(const std::vector<uint8_t>& effecterData, 197 uint8_t effecterCount) 198 { 199 std::vector<set_effecter_state_field> stateField; 200 201 if (effecterData.size() != effecterCount * 2) 202 { 203 return std::nullopt; 204 } 205 206 for (uint8_t i = 0; i < effecterCount; ++i) 207 { 208 uint8_t set_request = effecterData[i * 2] == PLDM_REQUEST_SET 209 ? PLDM_REQUEST_SET 210 : PLDM_NO_CHANGE; 211 set_effecter_state_field filed{set_request, effecterData[i * 2 + 1]}; 212 stateField.emplace_back(std::move(filed)); 213 } 214 215 return std::make_optional(std::move(stateField)); 216 } 217 218 std::string DBusHandler::getService(const char* path, 219 const char* interface) const 220 { 221 using DbusInterfaceList = std::vector<std::string>; 222 std::map<std::string, std::vector<std::string>> mapperResponse; 223 auto& bus = DBusHandler::getBus(); 224 225 auto mapper = bus.new_method_call(mapperBusName, mapperPath, 226 mapperInterface, "GetObject"); 227 mapper.append(path, DbusInterfaceList({interface})); 228 229 auto mapperResponseMsg = bus.call( 230 mapper, 231 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count()); 232 mapperResponseMsg.read(mapperResponse); 233 return mapperResponse.begin()->first; 234 } 235 236 GetSubTreeResponse 237 DBusHandler::getSubtree(const std::string& searchPath, int depth, 238 const std::vector<std::string>& ifaceList) const 239 { 240 auto& bus = pldm::utils::DBusHandler::getBus(); 241 auto method = bus.new_method_call(mapperBusName, mapperPath, 242 mapperInterface, "GetSubTree"); 243 method.append(searchPath, depth, ifaceList); 244 auto reply = bus.call( 245 method, 246 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count()); 247 GetSubTreeResponse response; 248 reply.read(response); 249 return response; 250 } 251 252 void reportError(const char* errorMsg) 253 { 254 static constexpr auto logObjPath = "/xyz/openbmc_project/logging"; 255 static constexpr auto logInterface = "xyz.openbmc_project.Logging.Create"; 256 257 auto& bus = pldm::utils::DBusHandler::getBus(); 258 259 try 260 { 261 auto service = DBusHandler().getService(logObjPath, logInterface); 262 using namespace sdbusplus::xyz::openbmc_project::Logging::server; 263 auto severity = 264 sdbusplus::xyz::openbmc_project::Logging::server::convertForMessage( 265 sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level:: 266 Error); 267 auto method = bus.new_method_call(service.c_str(), logObjPath, 268 logInterface, "Create"); 269 std::map<std::string, std::string> addlData{}; 270 method.append(errorMsg, severity, addlData); 271 bus.call_noreply(method); 272 } 273 catch (const std::exception& e) 274 { 275 error( 276 "failed to make a d-bus call to create error log, ERROR={ERR_EXCEP}", 277 "ERR_EXCEP", e.what()); 278 } 279 } 280 281 void DBusHandler::setDbusProperty(const DBusMapping& dBusMap, 282 const PropertyValue& value) const 283 { 284 auto setDbusValue = [&dBusMap, this](const auto& variant) { 285 auto& bus = getBus(); 286 auto service = getService(dBusMap.objectPath.c_str(), 287 dBusMap.interface.c_str()); 288 auto method = bus.new_method_call( 289 service.c_str(), dBusMap.objectPath.c_str(), dbusProperties, "Set"); 290 method.append(dBusMap.interface.c_str(), dBusMap.propertyName.c_str(), 291 variant); 292 bus.call_noreply(method); 293 }; 294 295 if (dBusMap.propertyType == "uint8_t") 296 { 297 std::variant<uint8_t> v = std::get<uint8_t>(value); 298 setDbusValue(v); 299 } 300 else if (dBusMap.propertyType == "bool") 301 { 302 std::variant<bool> v = std::get<bool>(value); 303 setDbusValue(v); 304 } 305 else if (dBusMap.propertyType == "int16_t") 306 { 307 std::variant<int16_t> v = std::get<int16_t>(value); 308 setDbusValue(v); 309 } 310 else if (dBusMap.propertyType == "uint16_t") 311 { 312 std::variant<uint16_t> v = std::get<uint16_t>(value); 313 setDbusValue(v); 314 } 315 else if (dBusMap.propertyType == "int32_t") 316 { 317 std::variant<int32_t> v = std::get<int32_t>(value); 318 setDbusValue(v); 319 } 320 else if (dBusMap.propertyType == "uint32_t") 321 { 322 std::variant<uint32_t> v = std::get<uint32_t>(value); 323 setDbusValue(v); 324 } 325 else if (dBusMap.propertyType == "int64_t") 326 { 327 std::variant<int64_t> v = std::get<int64_t>(value); 328 setDbusValue(v); 329 } 330 else if (dBusMap.propertyType == "uint64_t") 331 { 332 std::variant<uint64_t> v = std::get<uint64_t>(value); 333 setDbusValue(v); 334 } 335 else if (dBusMap.propertyType == "double") 336 { 337 std::variant<double> v = std::get<double>(value); 338 setDbusValue(v); 339 } 340 else if (dBusMap.propertyType == "string") 341 { 342 std::variant<std::string> v = std::get<std::string>(value); 343 setDbusValue(v); 344 } 345 else 346 { 347 throw std::invalid_argument("UnSpported Dbus Type"); 348 } 349 } 350 351 PropertyValue DBusHandler::getDbusPropertyVariant( 352 const char* objPath, const char* dbusProp, const char* dbusInterface) const 353 { 354 auto& bus = DBusHandler::getBus(); 355 auto service = getService(objPath, dbusInterface); 356 auto method = bus.new_method_call(service.c_str(), objPath, dbusProperties, 357 "Get"); 358 method.append(dbusInterface, dbusProp); 359 PropertyValue value{}; 360 auto reply = bus.call( 361 method, 362 std::chrono::duration_cast<microsec>(sec(DBUS_TIMEOUT)).count()); 363 reply.read(value); 364 return value; 365 } 366 367 PropertyValue jsonEntryToDbusVal(std::string_view type, 368 const nlohmann::json& value) 369 { 370 PropertyValue propValue{}; 371 if (type == "uint8_t") 372 { 373 propValue = static_cast<uint8_t>(value); 374 } 375 else if (type == "uint16_t") 376 { 377 propValue = static_cast<uint16_t>(value); 378 } 379 else if (type == "uint32_t") 380 { 381 propValue = static_cast<uint32_t>(value); 382 } 383 else if (type == "uint64_t") 384 { 385 propValue = static_cast<uint64_t>(value); 386 } 387 else if (type == "int16_t") 388 { 389 propValue = static_cast<int16_t>(value); 390 } 391 else if (type == "int32_t") 392 { 393 propValue = static_cast<int32_t>(value); 394 } 395 else if (type == "int64_t") 396 { 397 propValue = static_cast<int64_t>(value); 398 } 399 else if (type == "bool") 400 { 401 propValue = static_cast<bool>(value); 402 } 403 else if (type == "double") 404 { 405 propValue = static_cast<double>(value); 406 } 407 else if (type == "string") 408 { 409 propValue = static_cast<std::string>(value); 410 } 411 else 412 { 413 error("Unknown D-Bus property type, TYPE={OTHER_TYPE}", "OTHER_TYPE", 414 type); 415 } 416 417 return propValue; 418 } 419 420 uint16_t findStateEffecterId(const pldm_pdr* pdrRepo, uint16_t entityType, 421 uint16_t entityInstance, uint16_t containerId, 422 uint16_t stateSetId, bool localOrRemote) 423 { 424 uint8_t* pdrData = nullptr; 425 uint32_t pdrSize{}; 426 const pldm_pdr_record* record{}; 427 do 428 { 429 record = pldm_pdr_find_record_by_type(pdrRepo, PLDM_STATE_EFFECTER_PDR, 430 record, &pdrData, &pdrSize); 431 if (record && (localOrRemote ^ pldm_pdr_record_is_remote(record))) 432 { 433 auto pdr = reinterpret_cast<pldm_state_effecter_pdr*>(pdrData); 434 auto compositeEffecterCount = pdr->composite_effecter_count; 435 auto possible_states_start = pdr->possible_states; 436 437 for (auto effecters = 0x00; effecters < compositeEffecterCount; 438 effecters++) 439 { 440 auto possibleStates = 441 reinterpret_cast<state_effecter_possible_states*>( 442 possible_states_start); 443 auto setId = possibleStates->state_set_id; 444 auto possibleStateSize = possibleStates->possible_states_size; 445 446 if (entityType == pdr->entity_type && 447 entityInstance == pdr->entity_instance && 448 containerId == pdr->container_id && stateSetId == setId) 449 { 450 return pdr->effecter_id; 451 } 452 possible_states_start += possibleStateSize + sizeof(setId) + 453 sizeof(possibleStateSize); 454 } 455 } 456 } while (record); 457 458 return PLDM_INVALID_EFFECTER_ID; 459 } 460 461 int emitStateSensorEventSignal(uint8_t tid, uint16_t sensorId, 462 uint8_t sensorOffset, uint8_t eventState, 463 uint8_t previousEventState) 464 { 465 try 466 { 467 auto& bus = DBusHandler::getBus(); 468 auto msg = bus.new_signal("/xyz/openbmc_project/pldm", 469 "xyz.openbmc_project.PLDM.Event", 470 "StateSensorEvent"); 471 msg.append(tid, sensorId, sensorOffset, eventState, previousEventState); 472 473 msg.signal_send(); 474 } 475 catch (const std::exception& e) 476 { 477 error("Error emitting pldm event signal:ERROR={ERR_EXCEP}", "ERR_EXCEP", 478 e.what()); 479 return PLDM_ERROR; 480 } 481 482 return PLDM_SUCCESS; 483 } 484 485 uint16_t findStateSensorId(const pldm_pdr* pdrRepo, uint8_t tid, 486 uint16_t entityType, uint16_t entityInstance, 487 uint16_t containerId, uint16_t stateSetId) 488 { 489 auto pdrs = findStateSensorPDR(tid, entityType, stateSetId, pdrRepo); 490 for (auto pdr : pdrs) 491 { 492 auto sensorPdr = reinterpret_cast<pldm_state_sensor_pdr*>(pdr.data()); 493 auto compositeSensorCount = sensorPdr->composite_sensor_count; 494 auto possible_states_start = sensorPdr->possible_states; 495 496 for (auto sensors = 0x00; sensors < compositeSensorCount; sensors++) 497 { 498 auto possibleStates = 499 reinterpret_cast<state_sensor_possible_states*>( 500 possible_states_start); 501 auto setId = possibleStates->state_set_id; 502 auto possibleStateSize = possibleStates->possible_states_size; 503 if (entityType == sensorPdr->entity_type && 504 entityInstance == sensorPdr->entity_instance && 505 stateSetId == setId && containerId == sensorPdr->container_id) 506 { 507 return sensorPdr->sensor_id; 508 } 509 possible_states_start += possibleStateSize + sizeof(setId) + 510 sizeof(possibleStateSize); 511 } 512 } 513 return PLDM_INVALID_EFFECTER_ID; 514 } 515 516 void printBuffer(bool isTx, const std::vector<uint8_t>& buffer) 517 { 518 if (!buffer.empty()) 519 { 520 if (isTx) 521 { 522 std::cout << "Tx: "; 523 } 524 else 525 { 526 std::cout << "Rx: "; 527 } 528 std::ostringstream tempStream; 529 for (int byte : buffer) 530 { 531 tempStream << std::setfill('0') << std::setw(2) << std::hex << byte 532 << " "; 533 } 534 std::cout << tempStream.str() << std::endl; 535 } 536 } 537 538 std::string toString(const struct variable_field& var) 539 { 540 if (var.ptr == nullptr || !var.length) 541 { 542 return ""; 543 } 544 545 std::string str(reinterpret_cast<const char*>(var.ptr), var.length); 546 std::replace_if( 547 str.begin(), str.end(), [](const char& c) { return !isprint(c); }, ' '); 548 return str; 549 } 550 551 std::vector<std::string> split(std::string_view srcStr, std::string_view delim, 552 std::string_view trimStr) 553 { 554 std::vector<std::string> out; 555 size_t start; 556 size_t end = 0; 557 558 while ((start = srcStr.find_first_not_of(delim, end)) != std::string::npos) 559 { 560 end = srcStr.find(delim, start); 561 std::string_view dstStr = srcStr.substr(start, end - start); 562 if (!trimStr.empty()) 563 { 564 dstStr.remove_prefix(dstStr.find_first_not_of(trimStr)); 565 dstStr.remove_suffix(dstStr.size() - 1 - 566 dstStr.find_last_not_of(trimStr)); 567 } 568 569 if (!dstStr.empty()) 570 { 571 out.push_back(std::string(dstStr)); 572 } 573 } 574 575 return out; 576 } 577 578 std::string getCurrentSystemTime() 579 { 580 using namespace std::chrono; 581 const time_point<system_clock> tp = system_clock::now(); 582 std::time_t tt = system_clock::to_time_t(tp); 583 auto ms = duration_cast<microseconds>(tp.time_since_epoch()) - 584 duration_cast<seconds>(tp.time_since_epoch()); 585 586 std::stringstream ss; 587 ss << std::put_time(std::localtime(&tt), "%F %Z %T.") 588 << std::to_string(ms.count()); 589 return ss.str(); 590 } 591 592 bool checkForFruPresence(const std::string& objPath) 593 { 594 bool isPresent = false; 595 static constexpr auto presentInterface = 596 "xyz.openbmc_project.Inventory.Item"; 597 static constexpr auto presentProperty = "Present"; 598 try 599 { 600 auto propVal = pldm::utils::DBusHandler().getDbusPropertyVariant( 601 objPath.c_str(), presentProperty, presentInterface); 602 isPresent = std::get<bool>(propVal); 603 } 604 catch (const sdbusplus::exception::SdBusError& e) 605 { 606 error( 607 "Failed to check for FRU presence for {OBJ_PATH} ERROR = {ERR_EXCEP}", 608 "OBJ_PATH", objPath.c_str(), "ERR_EXCEP", e.what()); 609 } 610 return isPresent; 611 } 612 613 } // namespace utils 614 } // namespace pldm 615