Static layout: Implement PNOR code update

Implement the PNOR code update by pflash tool in openpower-pnor-update
service, and update the related associations.

Tested: Verify PNOR code update succeeds.

Change-Id: I53781d6420071200ac2ed6837f7a79bf5e1162c2
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/activation.cpp b/activation.cpp
index ce8cd07..ff48326 100644
--- a/activation.cpp
+++ b/activation.cpp
@@ -89,8 +89,7 @@
             (softwareServer::Activation::activation() ==
              softwareServer::Activation::Activations::Failed))
         {
-            Activation::activation(
-                softwareServer::Activation::Activations::Activating);
+            activation(softwareServer::Activation::Activations::Activating);
         }
     }
     return softwareServer::Activation::requestedActivation(value);
diff --git a/openpower-pnor-update@.service b/openpower-pnor-update@.service
new file mode 100644
index 0000000..b1db219
--- /dev/null
+++ b/openpower-pnor-update@.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=Update PNOR %I
+
+[Service]
+Type=oneshot
+RemainAfterExit=no
+ExecStart=/usr/sbin/pflash -E -f -p %I
+SyslogIdentifier=pflash
+
diff --git a/static/activation_static.cpp b/static/activation_static.cpp
index d73dc50..9768d41 100644
--- a/static/activation_static.cpp
+++ b/static/activation_static.cpp
@@ -1,23 +1,147 @@
 #include "activation_static.hpp"
 
+#include "item_updater.hpp"
+
+#include <filesystem>
+#include <phosphor-logging/log.hpp>
+
 namespace openpower
 {
 namespace software
 {
 namespace updater
 {
+namespace fs = std::filesystem;
 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
 
+using namespace phosphor::logging;
+
+auto ActivationStatic::activation(Activations value) -> Activations
+{
+
+    if (value != softwareServer::Activation::Activations::Active)
+    {
+        redundancyPriority.reset(nullptr);
+    }
+
+    if (value == softwareServer::Activation::Activations::Activating)
+    {
+        parent.freeSpace();
+        startActivation();
+        return softwareServer::Activation::activation(value);
+    }
+    else
+    {
+        activationBlocksTransition.reset(nullptr);
+        activationProgress.reset(nullptr);
+    }
+
+    return softwareServer::Activation::activation(value);
+}
+
 void ActivationStatic::startActivation()
 {
+    fs::path pnorFile;
+    fs::path imagePath(IMG_DIR);
+    imagePath /= versionId;
+
+    for (const auto& entry : fs::directory_iterator(imagePath))
+    {
+        if (entry.path().extension() == ".pnor")
+        {
+            pnorFile = entry;
+            break;
+        }
+    }
+    if (pnorFile.empty())
+    {
+        log<level::ERR>("Unable to find pnor file",
+                        entry("DIR=%s", imagePath.c_str()));
+        return;
+    }
+
+    if (!activationProgress)
+    {
+        activationProgress = std::make_unique<ActivationProgress>(bus, path);
+    }
+
+    if (!activationBlocksTransition)
+    {
+        activationBlocksTransition =
+            std::make_unique<ActivationBlocksTransition>(bus, path);
+    }
+
+    // TODO: check why the signal is still received without calling this
+    // function?
+    subscribeToSystemdSignals();
+
+    log<level::INFO>("Start programming...",
+                     entry("PNOR=%s", pnorFile.c_str()));
+
+    std::string pnorFileEscaped = pnorFile.string();
+    // Escape all '/' to '-'
+    std::replace(pnorFileEscaped.begin(), pnorFileEscaped.end(), '/', '-');
+
+    constexpr auto updatePNORService = "openpower-pnor-update@";
+    pnorUpdateUnit =
+        std::string(updatePNORService) + pnorFileEscaped + ".service";
+    auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
+                                      SYSTEMD_INTERFACE, "StartUnit");
+    method.append(pnorUpdateUnit, "replace");
+    bus.call_noreply(method);
+
+    activationProgress->progress(10);
 }
 
 void ActivationStatic::unitStateChange(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);
+
+    if (newStateUnit == pnorUpdateUnit)
+    {
+        if (newStateResult == "done")
+        {
+            finishActivation();
+        }
+        if (newStateResult == "failed" || newStateResult == "dependency")
+        {
+            Activation::activation(
+                softwareServer::Activation::Activations::Failed);
+        }
+    }
 }
 
 void ActivationStatic::finishActivation()
 {
+    activationProgress->progress(90);
+
+    // Set Redundancy Priority before setting to Active
+    if (!redundancyPriority)
+    {
+        redundancyPriority =
+            std::make_unique<RedundancyPriority>(bus, path, *this, 0);
+    }
+
+    activationProgress->progress(100);
+
+    activationBlocksTransition.reset();
+    activationProgress.reset();
+
+    unsubscribeFromSystemdSignals();
+    // Remove version object from image manager
+    deleteImageManagerObject();
+    // Create active association
+    parent.createActiveAssociation(path);
+    // Create functional assocaition
+    parent.updateFunctionalAssociation(versionId);
+
+    Activation::activation(Activation::Activations::Active);
 }
 
 } // namespace updater
diff --git a/static/activation_static.hpp b/static/activation_static.hpp
index 85914ac..6fe7138 100644
--- a/static/activation_static.hpp
+++ b/static/activation_static.hpp
@@ -17,11 +17,14 @@
   public:
     using Activation::Activation;
     ~ActivationStatic() = default;
+    Activations activation(Activations value) override;
 
   private:
     void unitStateChange(sdbusplus::message::message& msg) override;
     void startActivation() override;
     void finishActivation() override;
+
+    std::string pnorUpdateUnit;
 };
 
 } // namespace updater
diff --git a/static/item_updater_static.cpp b/static/item_updater_static.cpp
index 9ae2942..8eaf21d 100644
--- a/static/item_updater_static.cpp
+++ b/static/item_updater_static.cpp
@@ -10,6 +10,7 @@
 #include <fstream>
 #include <phosphor-logging/elog-errors.hpp>
 #include <phosphor-logging/log.hpp>
+#include <sstream>
 #include <string>
 
 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
@@ -210,7 +211,7 @@
 
 bool ItemUpdaterStatic::isVersionFunctional(const std::string& versionId)
 {
-    return true;
+    return versionId == functionalVersionId;
 }
 
 void ItemUpdaterStatic::freePriority(uint8_t value,
@@ -220,10 +221,30 @@
 
 void ItemUpdaterStatic::deleteAll()
 {
+    // Static layout has only one active and function pnor
+    // There is no implementation for this interface
 }
 
 void ItemUpdaterStatic::freeSpace()
 {
+    // For now assume static layout only has 1 active PNOR,
+    // so erase the active PNOR
+    for (const auto& iter : activations)
+    {
+        if (iter.second.get()->activation() ==
+            server::Activation::Activations::Active)
+        {
+            erase(iter.second->versionId);
+            break;
+        }
+    }
+}
+
+void ItemUpdaterStatic::updateFunctionalAssociation(
+    const std::string& versionId)
+{
+    functionalVersionId = versionId;
+    ItemUpdater::updateFunctionalAssociation(versionId);
 }
 
 void GardReset::reset()
diff --git a/static/item_updater_static.hpp b/static/item_updater_static.hpp
index dd7245f..75afeda 100644
--- a/static/item_updater_static.hpp
+++ b/static/item_updater_static.hpp
@@ -35,6 +35,8 @@
 
     void freeSpace() override;
 
+    void updateFunctionalAssociation(const std::string& versionId) override;
+
     bool isVersionFunctional(const std::string& versionId) override;
 
   private:
@@ -61,6 +63,9 @@
     /** @brief Host factory reset - clears PNOR partitions for each
      * Activation D-Bus object */
     void reset() override;
+
+    /** @brief The functional version ID */
+    std::string functionalVersionId;
 };
 
 } // namespace updater