| #include "activation.hpp" | 
 |  | 
 | #include "images.hpp" | 
 | #include "item_updater.hpp" | 
 | #include "msl_verify.hpp" | 
 | #include "serialize.hpp" | 
 |  | 
 | #include <boost/asio/io_context.hpp> | 
 | #include <boost/asio/post.hpp> | 
 | #include <phosphor-logging/elog-errors.hpp> | 
 | #include <phosphor-logging/elog.hpp> | 
 | #include <phosphor-logging/lg2.hpp> | 
 | #include <sdbusplus/exception.hpp> | 
 | #include <xyz/openbmc_project/Common/error.hpp> | 
 | #include <xyz/openbmc_project/Software/Version/error.hpp> | 
 |  | 
 | #ifdef WANT_SIGNATURE_VERIFY | 
 | #include "image_verify.hpp" | 
 | #endif | 
 |  | 
 | extern boost::asio::io_context& getIOContext(); | 
 |  | 
 | namespace phosphor | 
 | { | 
 | namespace software | 
 | { | 
 | namespace updater | 
 | { | 
 |  | 
 | namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; | 
 |  | 
 | PHOSPHOR_LOG2_USING; | 
 | using namespace phosphor::logging; | 
 | using InternalFailure = | 
 |     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; | 
 |  | 
 | #ifdef WANT_SIGNATURE_VERIFY | 
 | namespace control = sdbusplus::xyz::openbmc_project::Control::server; | 
 | #endif | 
 |  | 
 | void Activation::subscribeToSystemdSignals() | 
 | { | 
 |     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                             SYSTEMD_INTERFACE, "Subscribe"); | 
 |     try | 
 |     { | 
 |         this->bus.call_noreply(method); | 
 |     } | 
 |     catch (const sdbusplus::exception::exception& e) | 
 |     { | 
 |         if (e.name() != nullptr && | 
 |             strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0) | 
 |         { | 
 |             // If an Activation attempt fails, the Unsubscribe method is not | 
 |             // called. This may lead to an AlreadySubscribed error if the | 
 |             // Activation is re-attempted. | 
 |         } | 
 |         else | 
 |         { | 
 |             error("Error subscribing to systemd: {ERROR}", "ERROR", e); | 
 |         } | 
 |     } | 
 |  | 
 |     return; | 
 | } | 
 |  | 
 | void Activation::unsubscribeFromSystemdSignals() | 
 | { | 
 |     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                             SYSTEMD_INTERFACE, "Unsubscribe"); | 
 |     try | 
 |     { | 
 |         this->bus.call_noreply(method); | 
 |     } | 
 |     catch (const sdbusplus::exception::exception& e) | 
 |     { | 
 |         error("Error unsubscribing from systemd signals: {ERROR}", "ERROR", e); | 
 |     } | 
 |  | 
 |     return; | 
 | } | 
 |  | 
 | auto Activation::activation(Activations value) -> Activations | 
 | { | 
 |     if ((value != softwareServer::Activation::Activations::Active) && | 
 |         (value != softwareServer::Activation::Activations::Activating)) | 
 |     { | 
 |         redundancyPriority.reset(nullptr); | 
 |     } | 
 |  | 
 |     if (value == softwareServer::Activation::Activations::Activating) | 
 |     { | 
 | #ifdef WANT_SIGNATURE_VERIFY | 
 |         fs::path uploadDir(IMG_UPLOAD_DIR); | 
 |         if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) | 
 |         { | 
 |             onVerifyFailed(); | 
 |             // Stop the activation process, if fieldMode is enabled. | 
 |             if (parent.control::FieldMode::fieldModeEnabled()) | 
 |             { | 
 |                 return softwareServer::Activation::activation( | 
 |                     softwareServer::Activation::Activations::Failed); | 
 |             } | 
 |         } | 
 | #endif | 
 |  | 
 | #ifdef HOST_BIOS_UPGRADE | 
 |         auto purpose = parent.versions.find(versionId)->second->purpose(); | 
 |         if (purpose == VersionPurpose::Host) | 
 |         { | 
 |             if (!activationProgress) | 
 |             { | 
 |                 activationProgress = | 
 |                     std::make_unique<ActivationProgress>(bus, path); | 
 |             } | 
 |  | 
 |             // Enable systemd signals | 
 |             subscribeToSystemdSignals(); | 
 |  | 
 |             // Set initial progress | 
 |             activationProgress->progress(20); | 
 |  | 
 |             // Initiate image writing to flash | 
 |             flashWriteHost(); | 
 |  | 
 |             return softwareServer::Activation::activation(value); | 
 |         } | 
 | #endif | 
 |  | 
 |         auto versionStr = parent.versions.find(versionId)->second->version(); | 
 |  | 
 |         if (!minimum_ship_level::verify(versionStr)) | 
 |         { | 
 |             using namespace phosphor::logging; | 
 |             using IncompatibleErr = sdbusplus::xyz::openbmc_project::Software:: | 
 |                 Version::Error::Incompatible; | 
 |             using Incompatible = | 
 |                 xyz::openbmc_project::Software::Version::Incompatible; | 
 |  | 
 |             report<IncompatibleErr>( | 
 |                 prev_entry<Incompatible::MIN_VERSION>(), | 
 |                 prev_entry<Incompatible::ACTUAL_VERSION>(), | 
 |                 prev_entry<Incompatible::VERSION_PURPOSE>()); | 
 |             return softwareServer::Activation::activation( | 
 |                 softwareServer::Activation::Activations::Failed); | 
 |         } | 
 |  | 
 |         if (!activationProgress) | 
 |         { | 
 |             activationProgress = | 
 |                 std::make_unique<ActivationProgress>(bus, path); | 
 |         } | 
 |  | 
 |         if (!activationBlocksTransition) | 
 |         { | 
 |             activationBlocksTransition = | 
 |                 std::make_unique<ActivationBlocksTransition>(bus, path); | 
 |         } | 
 |  | 
 |         activationProgress->progress(10); | 
 |  | 
 |         parent.freeSpace(*this); | 
 |  | 
 |         // Enable systemd signals | 
 |         Activation::subscribeToSystemdSignals(); | 
 |  | 
 |         flashWrite(); | 
 |  | 
 | #if defined UBIFS_LAYOUT || defined MMC_LAYOUT | 
 |  | 
 |         return softwareServer::Activation::activation(value); | 
 |  | 
 | #else // STATIC_LAYOUT | 
 |  | 
 |         onFlashWriteSuccess(); | 
 |         return softwareServer::Activation::activation( | 
 |             softwareServer::Activation::Activations::Active); | 
 | #endif | 
 |     } | 
 |     else | 
 |     { | 
 |         activationBlocksTransition.reset(nullptr); | 
 |         activationProgress.reset(nullptr); | 
 |     } | 
 |     return softwareServer::Activation::activation(value); | 
 | } | 
 |  | 
 | void Activation::onFlashWriteSuccess() | 
 | { | 
 |     activationProgress->progress(100); | 
 |  | 
 |     activationBlocksTransition.reset(nullptr); | 
 |     activationProgress.reset(nullptr); | 
 |  | 
 |     rwVolumeCreated = false; | 
 |     roVolumeCreated = false; | 
 |     ubootEnvVarsUpdated = false; | 
 |     Activation::unsubscribeFromSystemdSignals(); | 
 |  | 
 |     storePurpose(versionId, parent.versions.find(versionId)->second->purpose()); | 
 |  | 
 |     if (!redundancyPriority) | 
 |     { | 
 |         redundancyPriority = | 
 |             std::make_unique<RedundancyPriority>(bus, path, *this, 0); | 
 |     } | 
 |  | 
 |     // Remove version object from image manager | 
 |     Activation::deleteImageManagerObject(); | 
 |  | 
 |     // Create active association | 
 |     parent.createActiveAssociation(path); | 
 |  | 
 |     // Create updateable association as this | 
 |     // can be re-programmed. | 
 |     parent.createUpdateableAssociation(path); | 
 |  | 
 |     if (Activation::checkApplyTimeImmediate() == true) | 
 |     { | 
 |         info("Image Active and ApplyTime is immediate; rebooting BMC."); | 
 |         Activation::rebootBmc(); | 
 |     } | 
 |     else | 
 |     { | 
 |         info("BMC image ready; need reboot to get activated."); | 
 |     } | 
 |  | 
 |     activation(softwareServer::Activation::Activations::Active); | 
 | } | 
 |  | 
 | void Activation::deleteImageManagerObject() | 
 | { | 
 |     // Call the Delete object for <versionID> inside image_manager | 
 |     auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(), | 
 |                                             "xyz.openbmc_project.Object.Delete", | 
 |                                             "Delete"); | 
 |     try | 
 |     { | 
 |         bus.call_noreply(method); | 
 |     } | 
 |     catch (const sdbusplus::exception::exception& e) | 
 |     { | 
 |         error("Error deleting image ({PATH}) from image manager: {ERROR}", | 
 |               "PATH", path, "ERROR", e); | 
 |         return; | 
 |     } | 
 | } | 
 |  | 
 | auto Activation::requestedActivation(RequestedActivations value) | 
 |     -> RequestedActivations | 
 | { | 
 |     rwVolumeCreated = false; | 
 |     roVolumeCreated = false; | 
 |     ubootEnvVarsUpdated = false; | 
 |  | 
 |     if ((value == softwareServer::Activation::RequestedActivations::Active) && | 
 |         (softwareServer::Activation::requestedActivation() != | 
 |          softwareServer::Activation::RequestedActivations::Active)) | 
 |     { | 
 |         if ((softwareServer::Activation::activation() == | 
 |              softwareServer::Activation::Activations::Ready) || | 
 |             (softwareServer::Activation::activation() == | 
 |              softwareServer::Activation::Activations::Failed)) | 
 |         { | 
 |             Activation::activation( | 
 |                 softwareServer::Activation::Activations::Activating); | 
 |         } | 
 |     } | 
 |     return softwareServer::Activation::requestedActivation(value); | 
 | } | 
 |  | 
 | uint8_t RedundancyPriority::priority(uint8_t value) | 
 | { | 
 |     // Set the priority value so that the freePriority() function can order | 
 |     // the versions by priority. | 
 |     auto newPriority = softwareServer::RedundancyPriority::priority(value); | 
 |     parent.parent.savePriority(parent.versionId, value); | 
 |     parent.parent.freePriority(value, parent.versionId); | 
 |     return newPriority; | 
 | } | 
 |  | 
 | uint8_t RedundancyPriority::sdbusPriority(uint8_t value) | 
 | { | 
 |     parent.parent.savePriority(parent.versionId, value); | 
 |     return softwareServer::RedundancyPriority::priority(value); | 
 | } | 
 |  | 
 | void Activation::unitStateChange(sdbusplus::message::message& msg) | 
 | { | 
 |     if (softwareServer::Activation::activation() != | 
 |         softwareServer::Activation::Activations::Activating) | 
 |     { | 
 |         return; | 
 |     } | 
 |  | 
 | #ifdef HOST_BIOS_UPGRADE | 
 |     auto purpose = parent.versions.find(versionId)->second->purpose(); | 
 |     if (purpose == VersionPurpose::Host) | 
 |     { | 
 |         onStateChangesBios(msg); | 
 |         return; | 
 |     } | 
 | #endif | 
 |  | 
 |     onStateChanges(msg); | 
 |  | 
 |     return; | 
 | } | 
 |  | 
 | #ifdef WANT_SIGNATURE_VERIFY | 
 | bool Activation::verifySignature(const fs::path& imageDir, | 
 |                                  const fs::path& confDir) | 
 | { | 
 |     using Signature = phosphor::software::image::Signature; | 
 |  | 
 |     Signature signature(imageDir, confDir); | 
 |  | 
 |     return signature.verify(); | 
 | } | 
 |  | 
 | void Activation::onVerifyFailed() | 
 | { | 
 |     error("Error occurred during image validation"); | 
 |     report<InternalFailure>(); | 
 | } | 
 | #endif | 
 |  | 
 | void ActivationBlocksTransition::enableRebootGuard() | 
 | { | 
 |     info("BMC image activating - BMC reboots are disabled."); | 
 |  | 
 |     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                       SYSTEMD_INTERFACE, "StartUnit"); | 
 |     method.append("reboot-guard-enable.service", "replace"); | 
 |     bus.call_noreply(method); | 
 | } | 
 |  | 
 | void ActivationBlocksTransition::disableRebootGuard() | 
 | { | 
 |     info("BMC activation has ended - BMC reboots are re-enabled."); | 
 |  | 
 |     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                       SYSTEMD_INTERFACE, "StartUnit"); | 
 |     method.append("reboot-guard-disable.service", "replace"); | 
 |     bus.call_noreply(method); | 
 | } | 
 |  | 
 | bool Activation::checkApplyTimeImmediate() | 
 | { | 
 |     auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf); | 
 |     if (service.empty()) | 
 |     { | 
 |         info("Error getting the service name for BMC image ApplyTime. " | 
 |              "The BMC needs to be manually rebooted to complete the image " | 
 |              "activation if needed immediately."); | 
 |     } | 
 |     else | 
 |     { | 
 |  | 
 |         auto method = bus.new_method_call(service.c_str(), applyTimeObjPath, | 
 |                                           dbusPropIntf, "Get"); | 
 |         method.append(applyTimeIntf, applyTimeProp); | 
 |  | 
 |         try | 
 |         { | 
 |             auto reply = bus.call(method); | 
 |  | 
 |             std::variant<std::string> result; | 
 |             reply.read(result); | 
 |             auto applyTime = std::get<std::string>(result); | 
 |             if (applyTime == applyTimeImmediate) | 
 |             { | 
 |                 return true; | 
 |             } | 
 |         } | 
 |         catch (const sdbusplus::exception::exception& e) | 
 |         { | 
 |             error("Error in getting ApplyTime: {ERROR}", "ERROR", e); | 
 |         } | 
 |     } | 
 |     return false; | 
 | } | 
 |  | 
 | #ifdef HOST_BIOS_UPGRADE | 
 | void Activation::flashWriteHost() | 
 | { | 
 |     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                       SYSTEMD_INTERFACE, "StartUnit"); | 
 |     auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; | 
 |     method.append(biosServiceFile, "replace"); | 
 |     try | 
 |     { | 
 |         auto reply = bus.call(method); | 
 |     } | 
 |     catch (const sdbusplus::exception::exception& e) | 
 |     { | 
 |         error("Error in trying to upgrade Host Bios: {ERROR}", "ERROR", e); | 
 |         report<InternalFailure>(); | 
 |     } | 
 | } | 
 |  | 
 | void Activation::onStateChangesBios(sdbusplus::message::message& msg) | 
 | { | 
 |     uint32_t newStateID{}; | 
 |     sdbusplus::message::object_path newStateObjPath; | 
 |     std::string newStateUnit{}; | 
 |     std::string newStateResult{}; | 
 |  | 
 |     // Read the msg and populate each variable | 
 |     msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); | 
 |  | 
 |     auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; | 
 |  | 
 |     if (newStateUnit == biosServiceFile) | 
 |     { | 
 |         // unsubscribe to systemd signals | 
 |         unsubscribeFromSystemdSignals(); | 
 |  | 
 |         if (newStateResult == "done") | 
 |         { | 
 |             // Set activation progress to 100 | 
 |             activationProgress->progress(100); | 
 |  | 
 |             // Set Activation value to active | 
 |             activation(softwareServer::Activation::Activations::Active); | 
 |  | 
 |             info("Bios upgrade completed successfully."); | 
 |             parent.biosVersion->version( | 
 |                 parent.versions.find(versionId)->second->version()); | 
 |  | 
 |             // Delete the uploaded activation | 
 |             boost::asio::post(getIOContext(), [this]() { | 
 |                 this->parent.erase(this->versionId); | 
 |             }); | 
 |         } | 
 |         else if (newStateResult == "failed") | 
 |         { | 
 |             // Set Activation value to Failed | 
 |             activation(softwareServer::Activation::Activations::Failed); | 
 |  | 
 |             error("Bios upgrade failed."); | 
 |         } | 
 |     } | 
 |  | 
 |     return; | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | void Activation::rebootBmc() | 
 | { | 
 |     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, | 
 |                                       SYSTEMD_INTERFACE, "StartUnit"); | 
 |     method.append("force-reboot.service", "replace"); | 
 |     try | 
 |     { | 
 |         auto reply = bus.call(method); | 
 |     } | 
 |     catch (const sdbusplus::exception::exception& e) | 
 |     { | 
 |         alert("Error in trying to reboot the BMC. The BMC needs to be manually " | 
 |               "rebooted to complete the image activation. {ERROR}", | 
 |               "ERROR", e); | 
 |         report<InternalFailure>(); | 
 |     } | 
 | } | 
 |  | 
 | } // namespace updater | 
 | } // namespace software | 
 | } // namespace phosphor |