1 #include "config.h" 2 3 #include "editor_impl.hpp" 4 5 #include "vpdecc/vpdecc.h" 6 7 #include "common_utility.hpp" 8 #include "ibm_vpd_utils.hpp" 9 #include "ipz_parser.hpp" 10 #include "parser_factory.hpp" 11 #include "vpd_exceptions.hpp" 12 13 #include <phosphor-logging/elog-errors.hpp> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 using namespace openpower::vpd::parser::interface; 17 using namespace openpower::vpd::constants; 18 using namespace openpower::vpd::parser::factory; 19 using namespace openpower::vpd::ipz::parser; 20 21 namespace openpower 22 { 23 namespace vpd 24 { 25 namespace manager 26 { 27 namespace editor 28 { 29 30 void EditorImpl::checkPTForRecord(Binary::const_iterator& iterator, 31 Byte ptLength) 32 { 33 // auto iterator = ptRecord.cbegin(); 34 auto end = std::next(iterator, ptLength + 1); 35 36 // Look at each entry in the PT keyword for the record name 37 while (iterator < end) 38 { 39 auto stop = std::next(iterator, lengths::RECORD_NAME); 40 std::string record(iterator, stop); 41 42 if (record == thisRecord.recName) 43 { 44 // Skip record name and record type 45 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType)); 46 47 // Get record offset 48 thisRecord.recOffset = readUInt16LE(iterator); 49 50 // pass the record offset length to read record length 51 std::advance(iterator, lengths::RECORD_OFFSET); 52 thisRecord.recSize = readUInt16LE(iterator); 53 54 std::advance(iterator, lengths::RECORD_LENGTH); 55 thisRecord.recECCoffset = readUInt16LE(iterator); 56 57 ECCLength len; 58 std::advance(iterator, lengths::RECORD_ECC_OFFSET); 59 len = readUInt16LE(iterator); 60 thisRecord.recECCLength = len; 61 62 // once we find the record we don't need to look further 63 return; 64 } 65 else 66 { 67 // Jump the record 68 std::advance(iterator, lengths::RECORD_NAME + sizeof(RecordType) + 69 sizeof(RecordOffset) + 70 sizeof(RecordLength) + 71 sizeof(ECCOffset) + sizeof(ECCLength)); 72 } 73 } 74 // imples the record was not found 75 throw std::runtime_error("Record not found"); 76 } 77 78 void EditorImpl::updateData(const Binary& kwdData) 79 { 80 std::size_t lengthToUpdate = kwdData.size() <= thisRecord.kwdDataLength 81 ? kwdData.size() 82 : thisRecord.kwdDataLength; 83 84 auto iteratorToNewdata = kwdData.cbegin(); 85 auto end = iteratorToNewdata; 86 std::advance(end, lengthToUpdate); 87 88 // update data in file buffer as it will be needed to update ECC 89 // avoiding extra stream operation here 90 auto iteratorToKWdData = vpdFile.begin(); 91 std::advance(iteratorToKWdData, thisRecord.kwDataOffset); 92 std::copy(iteratorToNewdata, end, iteratorToKWdData); 93 94 #ifdef ManagerTest 95 auto startItr = vpdFile.begin(); 96 std::advance(iteratorToKWdData, thisRecord.kwDataOffset); 97 auto endItr = startItr; 98 std::advance(endItr, thisRecord.kwdDataLength); 99 100 Binary updatedData(startItr, endItr); 101 if (updatedData == kwdData) 102 { 103 throw std::runtime_error("Data updated successfully"); 104 } 105 #else 106 107 // update data in EEPROM as well. As we will not write complete file back 108 vpdFileStream.seekp(startOffset + thisRecord.kwDataOffset, std::ios::beg); 109 110 iteratorToNewdata = kwdData.cbegin(); 111 std::copy(iteratorToNewdata, end, 112 std::ostreambuf_iterator<char>(vpdFileStream)); 113 114 // get a hold to new data in case encoding is needed 115 thisRecord.kwdUpdatedData.resize(thisRecord.kwdDataLength); 116 auto itrToKWdData = vpdFile.cbegin(); 117 std::advance(itrToKWdData, thisRecord.kwDataOffset); 118 auto kwdDataEnd = itrToKWdData; 119 std::advance(kwdDataEnd, thisRecord.kwdDataLength); 120 std::copy(itrToKWdData, kwdDataEnd, thisRecord.kwdUpdatedData.begin()); 121 #endif 122 } 123 124 void EditorImpl::checkRecordForKwd() 125 { 126 RecordOffset recOffset = thisRecord.recOffset; 127 128 // Amount to skip for record ID, size, and the RT keyword 129 constexpr auto skipBeg = sizeof(RecordId) + sizeof(RecordSize) + 130 lengths::KW_NAME + sizeof(KwSize); 131 132 auto iterator = vpdFile.cbegin(); 133 std::advance(iterator, recOffset + skipBeg + lengths::RECORD_NAME); 134 135 auto end = iterator; 136 std::advance(end, thisRecord.recSize); 137 std::size_t dataLength = 0; 138 139 while (iterator < end) 140 { 141 // Note keyword name 142 std::string kw(iterator, iterator + lengths::KW_NAME); 143 144 // Check if the Keyword starts with '#' 145 char kwNameStart = *iterator; 146 std::advance(iterator, lengths::KW_NAME); 147 148 // if keyword starts with # 149 if (POUND_KW == kwNameStart) 150 { 151 // Note existing keyword data length 152 dataLength = readUInt16LE(iterator); 153 154 // Jump past 2Byte keyword length + data 155 std::advance(iterator, sizeof(PoundKwSize)); 156 } 157 else 158 { 159 // Note existing keyword data length 160 dataLength = *iterator; 161 162 // Jump past keyword length and data 163 std::advance(iterator, sizeof(KwSize)); 164 } 165 166 if (thisRecord.recKWd == kw) 167 { 168 thisRecord.kwDataOffset = std::distance(vpdFile.cbegin(), iterator); 169 thisRecord.kwdDataLength = dataLength; 170 return; 171 } 172 173 // jump the data of current kwd to point to next kwd name 174 std::advance(iterator, dataLength); 175 } 176 177 throw std::runtime_error("Keyword not found"); 178 } 179 180 void EditorImpl::updateRecordECC() 181 { 182 auto itrToRecordData = vpdFile.cbegin(); 183 std::advance(itrToRecordData, thisRecord.recOffset); 184 185 auto itrToRecordECC = vpdFile.cbegin(); 186 std::advance(itrToRecordECC, thisRecord.recECCoffset); 187 188 auto l_status = vpdecc_create_ecc( 189 const_cast<uint8_t*>(&itrToRecordData[0]), thisRecord.recSize, 190 const_cast<uint8_t*>(&itrToRecordECC[0]), &thisRecord.recECCLength); 191 if (l_status != VPD_ECC_OK) 192 { 193 throw std::runtime_error("Ecc update failed"); 194 } 195 196 auto end = itrToRecordECC; 197 std::advance(end, thisRecord.recECCLength); 198 199 #ifndef ManagerTest 200 vpdFileStream.seekp(startOffset + thisRecord.recECCoffset, std::ios::beg); 201 std::copy(itrToRecordECC, end, 202 std::ostreambuf_iterator<char>(vpdFileStream)); 203 #endif 204 } 205 206 auto EditorImpl::getValue(offsets::Offsets offset) 207 { 208 auto itr = vpdFile.cbegin(); 209 std::advance(itr, offset); 210 LE2ByteData lowByte = *itr; 211 LE2ByteData highByte = *(itr + 1); 212 lowByte |= (highByte << 8); 213 214 return lowByte; 215 } 216 217 void EditorImpl::checkRecordData() 218 { 219 auto itrToRecordData = vpdFile.cbegin(); 220 std::advance(itrToRecordData, thisRecord.recOffset); 221 222 auto itrToRecordECC = vpdFile.cbegin(); 223 std::advance(itrToRecordECC, thisRecord.recECCoffset); 224 225 checkECC(itrToRecordData, itrToRecordECC, thisRecord.recSize, 226 thisRecord.recECCLength); 227 } 228 229 void EditorImpl::checkECC(Binary::const_iterator& itrToRecData, 230 Binary::const_iterator& itrToECCData, 231 RecordLength recLength, ECCLength eccLength) 232 { 233 auto l_status = 234 vpdecc_check_data(const_cast<uint8_t*>(&itrToRecData[0]), recLength, 235 const_cast<uint8_t*>(&itrToECCData[0]), eccLength); 236 237 if (l_status == VPD_ECC_CORRECTABLE_DATA) 238 { 239 try 240 { 241 if (vpdFileStream.is_open()) 242 { 243 vpdFileStream.seekp(startOffset + thisRecord.recOffset, 244 std::ios::beg); 245 auto end = itrToRecData; 246 std::advance(end, recLength); 247 std::copy(itrToRecData, end, 248 std::ostreambuf_iterator<char>(vpdFileStream)); 249 } 250 else 251 { 252 throw std::runtime_error("Ecc correction failed"); 253 } 254 } 255 catch (const std::fstream::failure& e) 256 { 257 std::cout << "Error while operating on file with exception"; 258 throw std::runtime_error("Ecc correction failed"); 259 } 260 } 261 else if (l_status != VPD_ECC_OK) 262 { 263 throw std::runtime_error("Ecc check failed"); 264 } 265 } 266 267 void EditorImpl::readVTOC() 268 { 269 // read VTOC offset 270 RecordOffset tocOffset = getValue(offsets::VTOC_PTR); 271 272 // read VTOC record length 273 RecordLength tocLength = getValue(offsets::VTOC_REC_LEN); 274 275 // read TOC ecc offset 276 ECCOffset tocECCOffset = getValue(offsets::VTOC_ECC_OFF); 277 278 // read TOC ecc length 279 ECCLength tocECCLength = getValue(offsets::VTOC_ECC_LEN); 280 281 auto itrToRecord = vpdFile.cbegin(); 282 std::advance(itrToRecord, tocOffset); 283 284 auto iteratorToECC = vpdFile.cbegin(); 285 std::advance(iteratorToECC, tocECCOffset); 286 287 // validate ecc for the record 288 checkECC(itrToRecord, iteratorToECC, tocLength, tocECCLength); 289 290 // to get to the record name. 291 std::advance(itrToRecord, sizeof(RecordId) + sizeof(RecordSize) + 292 // Skip past the RT keyword, which contains 293 // the record name. 294 lengths::KW_NAME + sizeof(KwSize)); 295 296 std::string recordName(itrToRecord, itrToRecord + lengths::RECORD_NAME); 297 298 if ("VTOC" != recordName) 299 { 300 throw std::runtime_error("VTOC record not found"); 301 } 302 303 // jump to length of PT kwd 304 std::advance(itrToRecord, lengths::RECORD_NAME + lengths::KW_NAME); 305 306 // Note size of PT 307 Byte ptLen = *itrToRecord; 308 std::advance(itrToRecord, 1); 309 310 checkPTForRecord(itrToRecord, ptLen); 311 } 312 313 template <typename T> 314 void EditorImpl::makeDbusCall(const std::string& object, 315 const std::string& interface, 316 const std::string& property, 317 const std::variant<T>& data) 318 { 319 auto bus = sdbusplus::bus::new_default(); 320 auto properties = 321 bus.new_method_call(INVENTORY_MANAGER_SERVICE, object.c_str(), 322 "org.freedesktop.DBus.Properties", "Set"); 323 properties.append(interface); 324 properties.append(property); 325 properties.append(data); 326 327 auto result = bus.call(properties); 328 329 if (result.is_method_error()) 330 { 331 throw std::runtime_error("bus call failed"); 332 } 333 } 334 335 void EditorImpl::processAndUpdateCI(const std::string& objectPath) 336 { 337 inventory::ObjectMap objects; 338 for (auto& commonInterface : jsonFile["commonInterfaces"].items()) 339 { 340 for (auto& ciPropertyList : commonInterface.value().items()) 341 { 342 if (ciPropertyList.value().type() == 343 nlohmann::json::value_t::object) 344 { 345 if ((ciPropertyList.value().value("recordName", "") == 346 thisRecord.recName) && 347 (ciPropertyList.value().value("keywordName", "") == 348 thisRecord.recKWd)) 349 { 350 inventory::PropertyMap prop; 351 inventory::InterfaceMap interfaces; 352 std::string kwdData(thisRecord.kwdUpdatedData.begin(), 353 thisRecord.kwdUpdatedData.end()); 354 355 prop.emplace(ciPropertyList.key(), std::move(kwdData)); 356 interfaces.emplace(commonInterface.key(), std::move(prop)); 357 objects.emplace(objectPath, std::move(interfaces)); 358 } 359 } 360 } 361 } 362 // Notify PIM 363 common::utility::callPIM(std::move(objects)); 364 } 365 366 void EditorImpl::processAndUpdateEI(const nlohmann::json& Inventory, 367 const inventory::Path& objPath) 368 { 369 inventory::ObjectMap objects; 370 for (const auto& extraInterface : Inventory["extraInterfaces"].items()) 371 { 372 if (extraInterface.value() != NULL) 373 { 374 for (const auto& eiPropertyList : extraInterface.value().items()) 375 { 376 if (eiPropertyList.value().type() == 377 nlohmann::json::value_t::object) 378 { 379 if ((eiPropertyList.value().value("recordName", "") == 380 thisRecord.recName) && 381 ((eiPropertyList.value().value("keywordName", "") == 382 thisRecord.recKWd))) 383 { 384 inventory::PropertyMap prop; 385 inventory::InterfaceMap interfaces; 386 std::string kwdData(thisRecord.kwdUpdatedData.begin(), 387 thisRecord.kwdUpdatedData.end()); 388 encodeKeyword(kwdData, eiPropertyList.value().value( 389 "encoding", "")); 390 391 prop.emplace(eiPropertyList.key(), std::move(kwdData)); 392 interfaces.emplace(extraInterface.key(), 393 std::move(prop)); 394 objects.emplace(objPath, std::move(interfaces)); 395 } 396 } 397 } 398 } 399 } 400 // Notify PIM 401 common::utility::callPIM(std::move(objects)); 402 } 403 404 void EditorImpl::updateCache() 405 { 406 const std::vector<nlohmann::json>& groupEEPROM = 407 jsonFile["frus"][vpdFilePath].get_ref<const nlohmann::json::array_t&>(); 408 409 inventory::ObjectMap objects; 410 // iterate through all the inventories for this file path 411 for (const auto& singleInventory : groupEEPROM) 412 { 413 inventory::PropertyMap prop; 414 inventory::InterfaceMap interfaces; 415 // by default inherit property is true 416 bool isInherit = true; 417 418 if (singleInventory.find("inherit") != singleInventory.end()) 419 { 420 isInherit = singleInventory["inherit"].get<bool>(); 421 } 422 423 if (isInherit) 424 { 425 prop.emplace(getDbusNameForThisKw(thisRecord.recKWd), 426 thisRecord.kwdUpdatedData); 427 interfaces.emplace( 428 (IPZ_INTERFACE + (std::string) "." + thisRecord.recName), 429 std::move(prop)); 430 objects.emplace( 431 (singleInventory["inventoryPath"].get<std::string>()), 432 std::move(interfaces)); 433 434 // process Common interface 435 processAndUpdateCI(singleInventory["inventoryPath"] 436 .get_ref<const nlohmann::json::string_t&>()); 437 } 438 439 // process extra interfaces 440 processAndUpdateEI(singleInventory, 441 singleInventory["inventoryPath"] 442 .get_ref<const nlohmann::json::string_t&>()); 443 444 // check if we need to copy some specific records in this case. 445 if (singleInventory.find("copyRecords") != singleInventory.end()) 446 { 447 if (find(singleInventory["copyRecords"].begin(), 448 singleInventory["copyRecords"].end(), 449 thisRecord.recName) != 450 singleInventory["copyRecords"].end()) 451 { 452 prop.emplace(thisRecord.recKWd, thisRecord.kwdUpdatedData); 453 interfaces.emplace( 454 (IPZ_INTERFACE + std::string{"."} + thisRecord.recName), 455 std::move(prop)); 456 objects.emplace( 457 (singleInventory["inventoryPath"].get<std::string>()), 458 std::move(interfaces)); 459 } 460 } 461 } 462 // Notify PIM 463 common::utility::callPIM(std::move(objects)); 464 } 465 466 void EditorImpl::expandLocationCode(const std::string& locationCodeType) 467 { 468 std::string propertyFCorTM{}; 469 std::string propertySE{}; 470 471 if (locationCodeType == "fcs") 472 { 473 propertyFCorTM = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VCEN", 474 "FC"); 475 propertySE = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VCEN", 476 "SE"); 477 } 478 else if (locationCodeType == "mts") 479 { 480 propertyFCorTM = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", 481 "TM"); 482 propertySE = readBusProperty(SYSTEM_OBJECT, "com.ibm.ipzvpd.VSYS", 483 "SE"); 484 } 485 486 const nlohmann::json& groupFRUS = 487 jsonFile["frus"].get_ref<const nlohmann::json::object_t&>(); 488 inventory::ObjectMap objects; 489 490 for (const auto& itemFRUS : groupFRUS.items()) 491 { 492 const std::vector<nlohmann::json>& groupEEPROM = 493 itemFRUS.value().get_ref<const nlohmann::json::array_t&>(); 494 for (const auto& itemEEPROM : groupEEPROM) 495 { 496 inventory::PropertyMap prop; 497 inventory::InterfaceMap interfaces; 498 const auto& objectPath = itemEEPROM["inventoryPath"]; 499 sdbusplus::message::object_path object(objectPath); 500 501 // check if the given item implements location code interface 502 if (itemEEPROM["extraInterfaces"].find(IBM_LOCATION_CODE_INF) != 503 itemEEPROM["extraInterfaces"].end()) 504 { 505 const std::string& unexpandedLocationCode = 506 itemEEPROM["extraInterfaces"][IBM_LOCATION_CODE_INF] 507 ["LocationCode"] 508 .get_ref<const nlohmann::json::string_t&>(); 509 std::size_t idx = unexpandedLocationCode.find(locationCodeType); 510 if (idx != std::string::npos) 511 { 512 std::string expandedLocationCode(unexpandedLocationCode); 513 514 if (locationCodeType == "fcs") 515 { 516 expandedLocationCode.replace( 517 idx, 3, 518 propertyFCorTM.substr(0, 4) + ".ND0." + propertySE); 519 } 520 else if (locationCodeType == "mts") 521 { 522 std::replace(propertyFCorTM.begin(), 523 propertyFCorTM.end(), '-', '.'); 524 expandedLocationCode.replace( 525 idx, 3, propertyFCorTM + "." + propertySE); 526 } 527 528 // update the DBUS interface COM as well as XYZ path 529 prop.emplace("LocationCode", expandedLocationCode); 530 // TODO depricate this com.ibm interface later 531 interfaces.emplace(IBM_LOCATION_CODE_INF, prop); 532 interfaces.emplace(XYZ_LOCATION_CODE_INF, std::move(prop)); 533 } 534 } 535 objects.emplace(std::move(object), std::move(interfaces)); 536 } 537 } 538 // Notify PIM 539 common::utility::callPIM(std::move(objects)); 540 } 541 542 #ifndef ManagerTest 543 static void enableRebootGuard() 544 { 545 try 546 { 547 auto bus = sdbusplus::bus::new_default(); 548 auto method = bus.new_method_call( 549 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 550 "org.freedesktop.systemd1.Manager", "StartUnit"); 551 method.append("reboot-guard-enable.service", "replace"); 552 bus.call_noreply(method); 553 } 554 catch (const sdbusplus::exception_t& e) 555 { 556 std::string errMsg = 557 "Bus call to enable BMC reboot failed for reason: "; 558 errMsg += e.what(); 559 560 throw std::runtime_error(errMsg); 561 } 562 } 563 564 static void disableRebootGuard() 565 { 566 try 567 { 568 auto bus = sdbusplus::bus::new_default(); 569 auto method = bus.new_method_call( 570 "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 571 "org.freedesktop.systemd1.Manager", "StartUnit"); 572 method.append("reboot-guard-disable.service", "replace"); 573 bus.call_noreply(method); 574 } 575 catch (const sdbusplus::exception_t& e) 576 { 577 using namespace phosphor::logging; 578 using InternalFailure = 579 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 580 581 std::string errMsg = 582 "Bus call to disable BMC reboot failed for reason: "; 583 errMsg += e.what(); 584 585 log<level::ERR>("Disable boot guard failed"); 586 elog<InternalFailure>(); 587 588 throw std::runtime_error(errMsg); 589 } 590 } 591 #endif 592 593 void EditorImpl::updateKeyword(const Binary& kwdData, uint32_t offset, 594 const bool& updCache) 595 { 596 try 597 { 598 startOffset = offset; 599 #ifndef ManagerTest 600 // Restrict BMC from rebooting when VPD is being written. This will 601 // prevent any data/ECC corruption in case BMC reboots while VPD update. 602 enableRebootGuard(); 603 604 Binary completeVPDFile; 605 vpdFileStream.exceptions(std::ifstream::badbit | 606 std::ifstream::failbit); 607 try 608 { 609 vpdFileStream.open(vpdFilePath, 610 std::ios::in | std::ios::out | std::ios::binary); 611 612 auto vpdFileSize = std::min(std::filesystem::file_size(vpdFilePath), 613 MAX_VPD_SIZE); 614 if (vpdFileSize == 0) 615 { 616 std::cerr << "File size is 0 for " << vpdFilePath << std::endl; 617 throw std::runtime_error("File size is 0."); 618 } 619 620 completeVPDFile.resize(vpdFileSize); 621 vpdFileStream.seekg(startOffset, std::ios_base::cur); 622 vpdFileStream.read(reinterpret_cast<char*>(&completeVPDFile[0]), 623 vpdFileSize); 624 vpdFileStream.clear(std::ios_base::eofbit); 625 } 626 catch (const std::system_error& fail) 627 { 628 std::cerr << "Exception in file handling [" << vpdFilePath 629 << "] error : " << fail.what(); 630 std::cerr << "Stream file size = " << vpdFileStream.gcount() 631 << std::endl; 632 throw; 633 } 634 vpdFile = completeVPDFile; 635 636 if (objPath.empty() && 637 jsonFile["frus"].find(vpdFilePath) != jsonFile["frus"].end()) 638 { 639 objPath = jsonFile["frus"][vpdFilePath][0]["inventoryPath"] 640 .get_ref<const nlohmann::json::string_t&>(); 641 } 642 643 #else 644 645 Binary completeVPDFile = vpdFile; 646 647 #endif 648 if (vpdFile.empty()) 649 { 650 throw std::runtime_error("Invalid File"); 651 } 652 auto iterator = vpdFile.cbegin(); 653 std::advance(iterator, IPZ_DATA_START); 654 655 Byte vpdType = *iterator; 656 if (vpdType == KW_VAL_PAIR_START_TAG) 657 { 658 // objPath should be empty only in case of test run. 659 ParserInterface* Iparser = ParserFactory::getParser( 660 completeVPDFile, objPath, vpdFilePath, startOffset); 661 IpzVpdParser* ipzParser = dynamic_cast<IpzVpdParser*>(Iparser); 662 663 try 664 { 665 if (ipzParser == nullptr) 666 { 667 throw std::runtime_error("Invalid cast"); 668 } 669 670 ipzParser->processHeader(); 671 delete ipzParser; 672 ipzParser = nullptr; 673 // ParserFactory::freeParser(Iparser); 674 675 // process VTOC for PTT rkwd 676 readVTOC(); 677 678 // check record for keywrod 679 checkRecordForKwd(); 680 681 // Check Data before updating 682 checkRecordData(); 683 684 // update the data to the file 685 updateData(kwdData); 686 687 // update the ECC data for the record once data has been updated 688 updateRecordECC(); 689 690 if (updCache) 691 { 692 #ifndef ManagerTest 693 // update the cache once data has been updated 694 updateCache(); 695 #endif 696 } 697 } 698 catch (const std::exception& e) 699 { 700 if (ipzParser != nullptr) 701 { 702 delete ipzParser; 703 } 704 throw std::runtime_error(e.what()); 705 } 706 707 #ifndef ManagerTest 708 // Once VPD data and Ecc update is done, disable BMC boot guard. 709 disableRebootGuard(); 710 #endif 711 712 return; 713 } 714 else 715 { 716 throw openpower::vpd::exceptions::VpdDataException( 717 "Could not find start tag in VPD " + vpdFilePath); 718 } 719 } 720 catch (const std::exception& e) 721 { 722 #ifndef ManagerTest 723 // Disable reboot guard. 724 disableRebootGuard(); 725 #endif 726 727 throw std::runtime_error(e.what()); 728 } 729 } 730 } // namespace editor 731 } // namespace manager 732 } // namespace vpd 733 } // namespace openpower 734