1 #include "inband_code_update.hpp" 2 3 #include "libpldmresponder/pdr.hpp" 4 #include "oem_ibm_handler.hpp" 5 #include "xyz/openbmc_project/Common/error.hpp" 6 7 #include <arpa/inet.h> 8 #include <libpldm/entity.h> 9 10 #include <nlohmann/json.hpp> 11 #include <phosphor-logging/lg2.hpp> 12 #include <sdbusplus/server.hpp> 13 #include <xyz/openbmc_project/Dump/NewDump/server.hpp> 14 15 #include <exception> 16 #include <fstream> 17 18 PHOSPHOR_LOG2_USING; 19 20 namespace pldm 21 { 22 using namespace utils; 23 24 namespace responder 25 { 26 using namespace oem_ibm_platform; 27 28 /** @brief Directory where the lid files without a header are stored */ 29 auto lidDirPath = fs::path(LID_STAGING_DIR) / "lid"; 30 31 /** @brief Directory where the image files are stored as they are built */ 32 auto imageDirPath = fs::path(LID_STAGING_DIR) / "image"; 33 34 /** @brief Directory where the code update tarball files are stored */ 35 auto updateDirPath = fs::path(LID_STAGING_DIR) / "update"; 36 37 /** @brief The file name of the code update tarball */ 38 constexpr auto tarImageName = "image.tar"; 39 40 /** @brief The file name of the hostfw image */ 41 constexpr auto hostfwImageName = "image-hostfw"; 42 43 /** @brief The filename of the file where bootside data will be saved */ 44 constexpr auto bootSideFileName = "bootSide"; 45 46 /** @brief The path to the code update tarball file */ 47 auto tarImagePath = fs::path(imageDirPath) / tarImageName; 48 49 /** @brief The path to the hostfw image */ 50 auto hostfwImagePath = fs::path(imageDirPath) / hostfwImageName; 51 52 /** @brief The path to the tarball file expected by the phosphor software 53 * manager */ 54 auto updateImagePath = fs::path("/tmp/images") / tarImageName; 55 56 /** @brief Current boot side */ 57 constexpr auto bootSideAttrName = "fw_boot_side_current"; 58 59 /** @brief Next boot side */ 60 constexpr auto bootNextSideAttrName = "fw_boot_side"; 61 62 /** @brief The filepath of file where bootside data will be saved */ 63 auto bootSideDirPath = fs::path("/var/lib/pldm/") / bootSideFileName; 64 65 std::string CodeUpdate::fetchCurrentBootSide() 66 { 67 return currBootSide; 68 } 69 70 std::string CodeUpdate::fetchNextBootSide() 71 { 72 return nextBootSide; 73 } 74 75 int CodeUpdate::setCurrentBootSide(const std::string& currSide) 76 { 77 currBootSide = currSide; 78 return PLDM_SUCCESS; 79 } 80 81 int CodeUpdate::setNextBootSide(const std::string& nextSide) 82 { 83 info("setNextBootSide, nextSide={NXT_SIDE}", "NXT_SIDE", nextSide); 84 pldm_boot_side_data pldmBootSideData = readBootSideFile(); 85 currBootSide = 86 (pldmBootSideData.current_boot_side == "Perm" ? Pside : Tside); 87 nextBootSide = nextSide; 88 pldmBootSideData.next_boot_side = (nextSide == Pside ? "Perm" : "Temp"); 89 std::string objPath{}; 90 if (nextBootSide == currBootSide) 91 { 92 info( 93 "Current bootside is same as next boot side, setting priority of running version 0"); 94 objPath = runningVersion; 95 } 96 else 97 { 98 info( 99 "Current bootside is not same as next boot side, setting priority of non running version 0"); 100 objPath = nonRunningVersion; 101 } 102 if (objPath.empty()) 103 { 104 error("no nonRunningVersion present"); 105 return PLDM_PLATFORM_INVALID_STATE_VALUE; 106 } 107 108 try 109 { 110 auto priorityPropValue = dBusIntf->getDbusPropertyVariant( 111 objPath.c_str(), "Priority", redundancyIntf); 112 const auto& priorityValue = std::get<uint8_t>(priorityPropValue); 113 if (priorityValue == 0) 114 { 115 // Requested next boot side is already set 116 return PLDM_SUCCESS; 117 } 118 } 119 catch (const std::exception& e) 120 { 121 // Alternate side may not be present due to a failed code update 122 error("Alternate side may not be present due to a failed code update. " 123 "ERROR: {ERR}", 124 "ERR", e); 125 return PLDM_PLATFORM_INVALID_STATE_VALUE; 126 } 127 128 pldm::utils::DBusMapping dbusMapping{objPath, redundancyIntf, "Priority", 129 "uint8_t"}; 130 uint8_t val = 0; 131 pldm::utils::PropertyValue value = static_cast<uint8_t>(val); 132 try 133 { 134 dBusIntf->setDbusProperty(dbusMapping, value); 135 } 136 catch (const std::exception& e) 137 { 138 error("Failed to set the next boot side to {PATH} , error - {ERROR}", 139 "PATH", objPath, "ERROR", e); 140 return PLDM_ERROR; 141 } 142 writeBootSideFile(pldmBootSideData); 143 return PLDM_SUCCESS; 144 } 145 146 int CodeUpdate::setRequestedApplyTime() 147 { 148 int rc = PLDM_SUCCESS; 149 pldm::utils::PropertyValue value = 150 "xyz.openbmc_project.Software.ApplyTime.RequestedApplyTimes.OnReset"; 151 DBusMapping dbusMapping; 152 dbusMapping.objectPath = "/xyz/openbmc_project/software/apply_time"; 153 dbusMapping.interface = "xyz.openbmc_project.Software.ApplyTime"; 154 dbusMapping.propertyName = "RequestedApplyTime"; 155 dbusMapping.propertyType = "string"; 156 try 157 { 158 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 159 } 160 catch (const std::exception& e) 161 { 162 error( 163 "Failed to set property '{PROPERTY}' at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}", 164 "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface, 165 "PROPERTY", dbusMapping.propertyName, "ERROR", e); 166 rc = PLDM_ERROR; 167 } 168 return rc; 169 } 170 171 int CodeUpdate::setRequestedActivation() 172 { 173 int rc = PLDM_SUCCESS; 174 pldm::utils::PropertyValue value = 175 "xyz.openbmc_project.Software.Activation.RequestedActivations.Active"; 176 DBusMapping dbusMapping; 177 dbusMapping.objectPath = newImageId; 178 dbusMapping.interface = "xyz.openbmc_project.Software.Activation"; 179 dbusMapping.propertyName = "RequestedActivation"; 180 dbusMapping.propertyType = "string"; 181 try 182 { 183 pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value); 184 } 185 catch (const std::exception& e) 186 { 187 error( 188 "Failed to set property {PROPERTY} at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}", 189 "PATH", dbusMapping.objectPath, "INTERFACE", dbusMapping.interface, 190 "PROPERTY", dbusMapping.propertyName, "ERROR", e); 191 rc = PLDM_ERROR; 192 } 193 return rc; 194 } 195 196 void CodeUpdate::setVersions() 197 { 198 PendingAttributesList biosAttrList; 199 static constexpr auto mapperService = "xyz.openbmc_project.ObjectMapper"; 200 static constexpr auto functionalObjPath = 201 "/xyz/openbmc_project/software/functional"; 202 static constexpr auto activeObjPath = 203 "/xyz/openbmc_project/software/active"; 204 static constexpr auto propIntf = "org.freedesktop.DBus.Properties"; 205 206 auto& bus = dBusIntf->getBus(); 207 try 208 { 209 auto method = bus.new_method_call(mapperService, functionalObjPath, 210 propIntf, "Get"); 211 method.append("xyz.openbmc_project.Association", "endpoints"); 212 std::variant<std::vector<std::string>> paths; 213 214 auto reply = bus.call(method, dbusTimeout); 215 reply.read(paths); 216 217 runningVersion = std::get<std::vector<std::string>>(paths)[0]; 218 219 auto method1 = 220 bus.new_method_call(mapperService, activeObjPath, propIntf, "Get"); 221 method1.append("xyz.openbmc_project.Association", "endpoints"); 222 223 auto reply1 = bus.call(method1, dbusTimeout); 224 reply1.read(paths); 225 for (const auto& path : std::get<std::vector<std::string>>(paths)) 226 { 227 if (path != runningVersion) 228 { 229 nonRunningVersion = path; 230 break; 231 } 232 } 233 if (!fs::exists(bootSideDirPath)) 234 { 235 pldm_boot_side_data pldmBootSideData; 236 std::string nextBootSideBiosValue = "Temp"; 237 auto attributeValue = getBiosAttrValue<std::string>("fw_boot_side"); 238 239 // We enter this path during Genesis boot/boot after Factory reset. 240 // PLDM waits for Entity manager to populate System Type. After 241 // receiving system Type from EM it populates the bios attributes 242 // specific to that system We do not have bios attributes populated 243 // when we reach here so setting it to default value of the 244 // attribute as mentioned in the json files. 245 if (attributeValue.has_value()) 246 { 247 nextBootSideBiosValue = attributeValue.value(); 248 } 249 else 250 { 251 info( 252 "Boot side is not initialized yet, so setting default value"); 253 nextBootSideBiosValue = "Temp"; 254 } 255 pldmBootSideData.current_boot_side = nextBootSideBiosValue; 256 pldmBootSideData.next_boot_side = nextBootSideBiosValue; 257 pldmBootSideData.running_version_object = runningVersion; 258 259 writeBootSideFile(pldmBootSideData); 260 biosAttrList.emplace_back(std::make_pair( 261 bootSideAttrName, 262 std::make_tuple(EnumAttribute, 263 pldmBootSideData.current_boot_side))); 264 biosAttrList.push_back(std::make_pair( 265 bootNextSideAttrName, 266 std::make_tuple(EnumAttribute, 267 pldmBootSideData.next_boot_side))); 268 setBiosAttr(biosAttrList); 269 } 270 else 271 { 272 pldm_boot_side_data pldmBootSideData = readBootSideFile(); 273 if (pldmBootSideData.running_version_object != runningVersion) 274 { 275 info( 276 "BMC have booted with the new image runningPath={RUNN_PATH}", 277 "RUNN_PATH", runningVersion.c_str()); 278 info("Previous Image was: {RUNN_VERS}", "RUNN_VERS", 279 pldmBootSideData.running_version_object); 280 auto current_boot_side = 281 (pldmBootSideData.current_boot_side == "Temp" ? "Perm" 282 : "Temp"); 283 pldmBootSideData.current_boot_side = current_boot_side; 284 pldmBootSideData.next_boot_side = current_boot_side; 285 pldmBootSideData.running_version_object = runningVersion; 286 writeBootSideFile(pldmBootSideData); 287 biosAttrList.emplace_back(std::make_pair( 288 bootSideAttrName, 289 std::make_tuple(EnumAttribute, 290 pldmBootSideData.current_boot_side))); 291 biosAttrList.push_back(std::make_pair( 292 bootNextSideAttrName, 293 std::make_tuple(EnumAttribute, 294 pldmBootSideData.next_boot_side))); 295 setBiosAttr(biosAttrList); 296 } 297 else 298 { 299 info( 300 "BMC have booted with the previous image runningPath={RUNN_PATH}", 301 "RUNN_PATH", pldmBootSideData.running_version_object); 302 pldm_boot_side_data pldmBootSideData = readBootSideFile(); 303 pldmBootSideData.next_boot_side = 304 pldmBootSideData.current_boot_side; 305 writeBootSideFile(pldmBootSideData); 306 biosAttrList.emplace_back(std::make_pair( 307 bootSideAttrName, 308 std::make_tuple(EnumAttribute, 309 pldmBootSideData.current_boot_side))); 310 biosAttrList.push_back(std::make_pair( 311 bootNextSideAttrName, 312 std::make_tuple(EnumAttribute, 313 pldmBootSideData.next_boot_side))); 314 setBiosAttr(biosAttrList); 315 } 316 currBootSide = 317 (pldmBootSideData.current_boot_side == "Temp" ? Tside : Pside); 318 nextBootSide = 319 (pldmBootSideData.next_boot_side == "Temp" ? Tside : Pside); 320 } 321 } 322 catch (const std::exception& e) 323 { 324 error( 325 "Failed to make a d-bus call to Object Mapper Association, error - {ERROR}", 326 "ERROR", e); 327 return; 328 } 329 330 using namespace sdbusplus::bus::match::rules; 331 captureNextBootSideChange.push_back( 332 std::make_unique<sdbusplus::bus::match_t>( 333 pldm::utils::DBusHandler::getBus(), 334 propertiesChanged(runningVersion, redundancyIntf), 335 [this](sdbusplus::message_t& msg) { 336 DbusChangedProps props; 337 std::string iface; 338 msg.read(iface, props); 339 processPriorityChangeNotification(props); 340 })); 341 fwUpdateMatcher.push_back(std::make_unique<sdbusplus::bus::match_t>( 342 pldm::utils::DBusHandler::getBus(), 343 "interface='org.freedesktop.DBus.ObjectManager',type='signal'," 344 "member='InterfacesAdded',path='/xyz/openbmc_project/software'", 345 [this](sdbusplus::message_t& msg) { 346 DBusInterfaceAdded interfaces; 347 sdbusplus::message::object_path path; 348 msg.read(path, interfaces); 349 350 for (auto& interface : interfaces) 351 { 352 if (interface.first == 353 "xyz.openbmc_project.Software.Activation") 354 { 355 auto imageInterface = 356 "xyz.openbmc_project.Software.Activation"; 357 auto imageObjPath = path.str.c_str(); 358 359 try 360 { 361 auto propVal = dBusIntf->getDbusPropertyVariant( 362 imageObjPath, "Activation", imageInterface); 363 if (isCodeUpdateInProgress()) 364 { 365 newImageId = path.str; 366 if (!imageActivationMatch) 367 { 368 imageActivationMatch = std::make_unique< 369 sdbusplus::bus::match_t>( 370 pldm::utils::DBusHandler::getBus(), 371 propertiesChanged(newImageId, 372 "xyz.openbmc_project." 373 "Software.Activation"), 374 [this](sdbusplus::message_t& msg) { 375 DbusChangedProps props; 376 std::string iface; 377 msg.read(iface, props); 378 const auto itr = 379 props.find("Activation"); 380 if (itr != props.end()) 381 { 382 PropertyValue value = itr->second; 383 auto propVal = 384 std::get<std::string>(value); 385 if (propVal == 386 "xyz.openbmc_project.Software." 387 "Activation.Activations.Active") 388 { 389 CodeUpdateState state = 390 CodeUpdateState::END; 391 setCodeUpdateProgress(false); 392 auto sensorId = 393 getFirmwareUpdateSensor(); 394 sendStateSensorEvent( 395 sensorId, 396 PLDM_STATE_SENSOR_STATE, 0, 397 uint8_t(state), 398 uint8_t(CodeUpdateState:: 399 START)); 400 newImageId.clear(); 401 } 402 else if (propVal == 403 "xyz.openbmc_project." 404 "Software.Activation." 405 "Activations.Failed" || 406 propVal == 407 "xyz.openbmc_" 408 "project.Software." 409 "Activation." 410 "Activations." 411 "Invalid") 412 { 413 CodeUpdateState state = 414 CodeUpdateState::FAIL; 415 setCodeUpdateProgress(false); 416 auto sensorId = 417 getFirmwareUpdateSensor(); 418 sendStateSensorEvent( 419 sensorId, 420 PLDM_STATE_SENSOR_STATE, 0, 421 uint8_t(state), 422 uint8_t(CodeUpdateState:: 423 START)); 424 newImageId.clear(); 425 } 426 } 427 }); 428 } 429 auto rc = setRequestedActivation(); 430 if (rc != PLDM_SUCCESS) 431 { 432 error("Could not set Requested Activation"); 433 CodeUpdateState state = CodeUpdateState::FAIL; 434 setCodeUpdateProgress(false); 435 auto sensorId = getFirmwareUpdateSensor(); 436 sendStateSensorEvent( 437 sensorId, PLDM_STATE_SENSOR_STATE, 0, 438 uint8_t(state), 439 uint8_t(CodeUpdateState::START)); 440 } 441 break; 442 } 443 else 444 { 445 // Out of band update 446 processRenameEvent(); 447 } 448 } 449 catch (const sdbusplus::exception_t& e) 450 { 451 error( 452 "Failed to get activation status for interface '{INTERFACE}' and object path '{PATH}', error - {ERROR}", 453 "ERROR", e, "INTERFACE", imageInterface, "PATH", 454 imageObjPath); 455 } 456 } 457 } 458 })); 459 } 460 461 void CodeUpdate::processRenameEvent() 462 { 463 info("Processing Rename Event"); 464 465 PendingAttributesList biosAttrList; 466 pldm_boot_side_data pldmBootSideData = readBootSideFile(); 467 pldmBootSideData.current_boot_side = "Perm"; 468 pldmBootSideData.next_boot_side = "Perm"; 469 470 currBootSide = Pside; 471 nextBootSide = Pside; 472 473 auto sensorId = getBootSideRenameStateSensor(); 474 info("Received sendor id for rename {ID}", "ID", sensorId); 475 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0, 476 PLDM_OEM_IBM_BOOT_SIDE_RENAME_STATE_RENAMED, 477 PLDM_OEM_IBM_BOOT_SIDE_RENAME_STATE_NOT_RENAMED); 478 writeBootSideFile(pldmBootSideData); 479 biosAttrList.emplace_back(std::make_pair( 480 bootSideAttrName, 481 std::make_tuple(EnumAttribute, pldmBootSideData.current_boot_side))); 482 biosAttrList.push_back(std::make_pair( 483 bootNextSideAttrName, 484 std::make_tuple(EnumAttribute, pldmBootSideData.next_boot_side))); 485 setBiosAttr(biosAttrList); 486 } 487 488 void CodeUpdate::writeBootSideFile(const pldm_boot_side_data& pldmBootSideData) 489 { 490 try 491 { 492 fs::create_directories(bootSideDirPath.parent_path()); 493 std::ofstream writeFile(bootSideDirPath, std::ios::out); 494 if (!writeFile.is_open()) 495 { 496 error("Failed to open bootside file {FILE} for writing", "FILE", 497 bootSideDirPath.string()); 498 return; 499 } 500 501 nlohmann::json data; 502 data["CurrentBootSide"] = pldmBootSideData.current_boot_side; 503 data["NextBootSide"] = pldmBootSideData.next_boot_side; 504 data["RunningObject"] = pldmBootSideData.running_version_object; 505 506 try 507 { 508 writeFile << data.dump(4); 509 } 510 catch (const nlohmann::json::exception& e) 511 { 512 error("JSON serialization for BootSide failed: {ERROR}", "ERROR", 513 e); 514 return; 515 } 516 517 writeFile.close(); 518 } 519 catch (const std::exception& e) 520 { 521 error("Error {ERROR] while writing bootside file: {FILE}", "ERROR", e, 522 "FILE", bootSideDirPath); 523 } 524 } 525 526 pldm_boot_side_data CodeUpdate::readBootSideFile() 527 { 528 pldm_boot_side_data pldmBootSideDataRead{}; 529 530 std::ifstream readFile(bootSideDirPath.string(), std::ios::in); 531 532 if (!readFile) 533 { 534 error("Failed to read Bootside file"); 535 return pldmBootSideDataRead; 536 } 537 538 nlohmann::json jsonBootSideData; 539 readFile >> jsonBootSideData; 540 541 pldm_boot_side_data data; 542 data.current_boot_side = jsonBootSideData.value("CurrentBootSide", ""); 543 data.next_boot_side = jsonBootSideData.value("NextBootSide", ""); 544 data.running_version_object = jsonBootSideData.value("RunningObject", ""); 545 546 readFile.close(); 547 548 return pldmBootSideDataRead; 549 } 550 551 void CodeUpdate::processPriorityChangeNotification( 552 const DbusChangedProps& chProperties) 553 { 554 static constexpr auto propName = "Priority"; 555 const auto it = chProperties.find(propName); 556 if (it == chProperties.end()) 557 { 558 return; 559 } 560 uint8_t newVal = std::get<uint8_t>(it->second); 561 nextBootSide = (newVal == 0) ? currBootSide 562 : ((currBootSide == Tside) ? Pside : Tside); 563 } 564 565 void CodeUpdate::setOemPlatformHandler( 566 pldm::responder::oem_platform::Handler* handler) 567 { 568 oemPlatformHandler = handler; 569 } 570 571 void CodeUpdate::clearDirPath(const std::string& dirPath) 572 { 573 if (!fs::is_directory(dirPath)) 574 { 575 error("The directory '{PATH}' does not exist", "PATH", dirPath); 576 return; 577 } 578 for (const auto& iter : fs::directory_iterator(dirPath)) 579 { 580 fs::remove_all(iter); 581 } 582 } 583 584 void CodeUpdate::sendStateSensorEvent( 585 uint16_t sensorId, enum sensor_event_class_states sensorEventClass, 586 uint8_t sensorOffset, uint8_t eventState, uint8_t prevEventState) 587 { 588 pldm::responder::oem_ibm_platform::Handler* oemIbmPlatformHandler = 589 dynamic_cast<pldm::responder::oem_ibm_platform::Handler*>( 590 oemPlatformHandler); 591 oemIbmPlatformHandler->sendStateSensorEvent( 592 sensorId, sensorEventClass, sensorOffset, eventState, prevEventState); 593 } 594 595 void CodeUpdate::deleteImage() 596 { 597 static constexpr auto UPDATER_SERVICE = 598 "xyz.openbmc_project.Software.BMC.Updater"; 599 static constexpr auto SW_OBJ_PATH = "/xyz/openbmc_project/software"; 600 static constexpr auto DELETE_INTF = 601 "xyz.openbmc_project.Collection.DeleteAll"; 602 603 auto& bus = dBusIntf->getBus(); 604 try 605 { 606 auto method = bus.new_method_call(UPDATER_SERVICE, SW_OBJ_PATH, 607 DELETE_INTF, "DeleteAll"); 608 bus.call_noreply(method, dbusTimeout); 609 } 610 catch (const std::exception& e) 611 { 612 error( 613 "Failed to delete image at path '{PATH}' and interface '{INTERFACE}', error - {ERROR}", 614 "PATH", SW_OBJ_PATH, "INTERFACE", DELETE_INTF, "ERROR", e); 615 return; 616 } 617 } 618 619 uint8_t fetchBootSide(uint16_t entityInstance, CodeUpdate* codeUpdate) 620 { 621 uint8_t sensorOpState = tSideNum; 622 if (entityInstance == 0) 623 { 624 auto currSide = codeUpdate->fetchCurrentBootSide(); 625 if (currSide == Pside) 626 { 627 sensorOpState = pSideNum; 628 } 629 } 630 else if (entityInstance == 1) 631 { 632 auto nextSide = codeUpdate->fetchNextBootSide(); 633 if (nextSide == Pside) 634 { 635 sensorOpState = pSideNum; 636 } 637 } 638 else 639 { 640 sensorOpState = PLDM_SENSOR_UNKNOWN; 641 } 642 643 return sensorOpState; 644 } 645 646 int setBootSide(uint16_t entityInstance, uint8_t currState, 647 const std::vector<set_effecter_state_field>& stateField, 648 CodeUpdate* codeUpdate) 649 { 650 int rc = PLDM_SUCCESS; 651 auto side = (stateField[currState].effecter_state == pSideNum) ? "P" : "T"; 652 653 if (entityInstance == 0) 654 { 655 rc = codeUpdate->setCurrentBootSide(side); 656 } 657 else if (entityInstance == 1) 658 { 659 rc = codeUpdate->setNextBootSide(side); 660 } 661 else 662 { 663 rc = PLDM_PLATFORM_INVALID_STATE_VALUE; 664 } 665 return rc; 666 } 667 668 template <typename... T> 669 int executeCmd(const T&... t) 670 { 671 std::stringstream cmd; 672 ((cmd << t << " "), ...) << std::endl; 673 FILE* pipe = popen(cmd.str().c_str(), "r"); 674 if (!pipe) 675 { 676 throw std::runtime_error("popen() failed!"); 677 } 678 int rc = pclose(pipe); 679 if (WEXITSTATUS(rc)) 680 { 681 std::cerr << "Error executing: "; 682 ((std::cerr << " " << t), ...); 683 std::cerr << "\n"; 684 return -1; 685 } 686 687 return 0; 688 } 689 690 int processCodeUpdateLid(const std::string& filePath) 691 { 692 struct LidHeader 693 { 694 uint16_t magicNumber; 695 uint16_t headerVersion; 696 uint32_t lidNumber; 697 uint32_t lidDate; 698 uint16_t lidTime; 699 uint16_t lidClass; 700 uint32_t lidCrc; 701 uint32_t lidSize; 702 uint32_t headerSize; 703 }; 704 LidHeader header; 705 706 std::ifstream ifs(filePath, std::ios::in | std::ios::binary); 707 if (!ifs) 708 { 709 error("Failed to opening file '{FILE}' ifstream", "PATH", filePath); 710 return PLDM_ERROR; 711 } 712 ifs.seekg(0); 713 ifs.read(reinterpret_cast<char*>(&header), sizeof(header)); 714 715 // File size should be the value of lid size minus the header size 716 auto fileSize = fs::file_size(filePath); 717 fileSize -= htonl(header.headerSize); 718 if (fileSize < htonl(header.lidSize)) 719 { 720 // File is not completely written yet 721 ifs.close(); 722 return PLDM_SUCCESS; 723 } 724 725 constexpr auto magicNumber = 0x0222; 726 if (htons(header.magicNumber) != magicNumber) 727 { 728 error("Invalid magic number for file '{PATH}'", "PATH", filePath); 729 ifs.close(); 730 return PLDM_ERROR; 731 } 732 733 fs::create_directories(imageDirPath); 734 fs::create_directories(lidDirPath); 735 736 constexpr auto bmcClass = 0x2000; 737 if (htons(header.lidClass) == bmcClass) 738 { 739 // Skip the header and concatenate the BMC LIDs into a tar file 740 std::ofstream ofs(tarImagePath, 741 std::ios::out | std::ios::binary | std::ios::app); 742 ifs.seekg(htonl(header.headerSize)); 743 ofs << ifs.rdbuf(); 744 ofs.flush(); 745 ofs.close(); 746 } 747 else 748 { 749 std::stringstream lidFileName; 750 lidFileName << std::hex << htonl(header.lidNumber) << ".lid"; 751 auto lidNoHeaderPath = fs::path(lidDirPath) / lidFileName.str(); 752 std::ofstream ofs(lidNoHeaderPath, 753 std::ios::out | std::ios::binary | std::ios::trunc); 754 ifs.seekg(htonl(header.headerSize)); 755 ofs << ifs.rdbuf(); 756 ofs.flush(); 757 ofs.close(); 758 } 759 760 ifs.close(); 761 fs::remove(filePath); 762 return PLDM_SUCCESS; 763 } 764 765 int CodeUpdate::assembleCodeUpdateImage() 766 { 767 pid_t pid = fork(); 768 769 if (pid == 0) 770 { 771 pid_t nextPid = fork(); 772 if (nextPid == 0) 773 { 774 // Create the hostfw squashfs image from the LID files without 775 // header 776 auto rc = executeCmd("/usr/sbin/mksquashfs", lidDirPath.c_str(), 777 hostfwImagePath.c_str(), "-all-root", 778 "-no-recovery"); 779 if (rc < 0) 780 { 781 error("Error occurred during the mksqusquashfs call"); 782 setCodeUpdateProgress(false); 783 auto sensorId = getFirmwareUpdateSensor(); 784 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0, 785 uint8_t(CodeUpdateState::FAIL), 786 uint8_t(CodeUpdateState::START)); 787 exit(EXIT_FAILURE); 788 } 789 790 fs::create_directories(updateDirPath); 791 792 // Extract the BMC tarball content 793 rc = executeCmd("/bin/tar", "-xf", tarImagePath.c_str(), "-C", 794 updateDirPath); 795 if (rc < 0) 796 { 797 setCodeUpdateProgress(false); 798 auto sensorId = getFirmwareUpdateSensor(); 799 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0, 800 uint8_t(CodeUpdateState::FAIL), 801 uint8_t(CodeUpdateState::START)); 802 exit(EXIT_FAILURE); 803 } 804 805 // Add the hostfw image to the directory where the contents were 806 // extracted 807 fs::copy_file(hostfwImagePath, 808 fs::path(updateDirPath) / hostfwImageName, 809 fs::copy_options::overwrite_existing); 810 811 // Remove the tarball file, then re-generate it with so that the 812 // hostfw image becomes part of the tarball 813 fs::remove(tarImagePath); 814 rc = executeCmd("/bin/tar", "-cf", tarImagePath, ".", "-C", 815 updateDirPath); 816 if (rc < 0) 817 { 818 error("Error occurred during the generation of the tarball"); 819 setCodeUpdateProgress(false); 820 auto sensorId = getFirmwareUpdateSensor(); 821 sendStateSensorEvent(sensorId, PLDM_STATE_SENSOR_STATE, 0, 822 uint8_t(CodeUpdateState::FAIL), 823 uint8_t(CodeUpdateState::START)); 824 exit(EXIT_FAILURE); 825 } 826 827 // Copy the tarball to the update directory to trigger the phosphor 828 // software manager to create a version interface 829 fs::copy_file(tarImagePath, updateImagePath, 830 fs::copy_options::overwrite_existing); 831 832 // Cleanup 833 fs::remove_all(updateDirPath); 834 fs::remove_all(lidDirPath); 835 fs::remove_all(imageDirPath); 836 837 exit(EXIT_SUCCESS); 838 } 839 else if (nextPid < 0) 840 { 841 error("Failure occurred during fork, error number - {ERROR_NUM}", 842 "ERROR_NUM", errno); 843 exit(EXIT_FAILURE); 844 } 845 846 // Do nothing as parent. When parent exits, child will be reparented 847 // under init and be reaped properly. 848 exit(0); 849 } 850 else if (pid > 0) 851 { 852 int status; 853 if (waitpid(pid, &status, 0) < 0) 854 { 855 error("Error occurred during waitpid, error number - {ERROR_NUM}", 856 "ERROR_NUM", errno); 857 858 return PLDM_ERROR; 859 } 860 else if (WEXITSTATUS(status) != 0) 861 { 862 error( 863 "Failed to execute the assembling of the image, status is {IMG_STATUS}", 864 "IMG_STATUS", status); 865 return PLDM_ERROR; 866 } 867 } 868 else 869 { 870 error("Error occurred during fork, error number - {ERROR_NUM}}", 871 "ERROR_NUM", errno); 872 return PLDM_ERROR; 873 } 874 875 return PLDM_SUCCESS; 876 } 877 878 } // namespace responder 879 } // namespace pldm 880