PHAL: enterMPIPL chip-op failure handling support

Added new enterMpReboot procedure for PHAL feature enabled
systems to support libphal library API based chip-op
failure handling

Existing enterMpReboot procedure moved to non PHAL enabled systems.

Tested:
 - Poitive Path 3 processor config
    Starting Start memory preserving reboot host0...
    Starting memory preserving reboot
    Enter MPIPL completed on proc(2)
    Enter MPIPL completed on proc(3)
    Enter MPIPL completed on proc(0)
    Finished Start memory preserving reboot host0.

 - Error Path:
  - Forced proc2 SBE invalid state.

Journal data
SBE (/proc2) is not ready for chip-op: state(0x00000000)

PEL data:
"SBE_ERR_MSG": "SBE chip-op not allowed",
 "Message": "chipop request failure reported by SBE",
  "SRC6": [
            "0x2A901",
            "[0:15] chip position, [16:23] command class,
            [24:31] command type"
        ]
    },

Signed-off-by: Jayanth Othayoth <ojayanth@in.ibm.com>
Change-Id: Ie0cb5abffd5116d44edfbbeb2fe3d8408bfb73e2
diff --git a/meson.build b/meson.build
index 38e7abd..4d52f5c 100644
--- a/meson.build
+++ b/meson.build
@@ -69,6 +69,7 @@
         'procedures/p9/set_sync_fsi_clock_mode.cpp',
         'procedures/p9/start_host.cpp',
         'procedures/p9/start_host_mpreboot.cpp',
+        'procedures/p9/enter_mpreboot.cpp',
     ]
 endif
 if build_openfsi
@@ -83,6 +84,7 @@
         'procedures/phal/proc_pre_poweroff.cpp',
         'procedures/phal/check_host_running.cpp',
         'procedures/phal/import_devtree.cpp',
+        'procedures/phal/enter_mpreboot.cpp',
         'extensions/phal/common_utils.cpp',
         'extensions/phal/pdbg_utils.cpp',
         'extensions/phal/create_pel.cpp',
@@ -116,7 +118,6 @@
         'targeting.cpp',
         'procedures/common/cfam_overrides.cpp',
         'procedures/common/cfam_reset.cpp',
-        'procedures/common/enter_mpreboot.cpp',
         'procedures/common/collect_sbe_hb_data.cpp',
         'util.cpp',
     ] + extra_sources,
diff --git a/procedures/common/enter_mpreboot.cpp b/procedures/p9/enter_mpreboot.cpp
similarity index 100%
rename from procedures/common/enter_mpreboot.cpp
rename to procedures/p9/enter_mpreboot.cpp
diff --git a/procedures/phal/enter_mpreboot.cpp b/procedures/phal/enter_mpreboot.cpp
new file mode 100644
index 0000000..aa7365e
--- /dev/null
+++ b/procedures/phal/enter_mpreboot.cpp
@@ -0,0 +1,180 @@
+/**
+ * Copyright (C) 2020 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "registration.hpp"
+
+extern "C"
+{
+#include <libpdbg.h>
+#include <libpdbg_sbe.h>
+}
+
+#include "extensions/phal/create_pel.hpp"
+
+#include <attributes_info.H>
+#include <fmt/format.h>
+#include <libphal.H>
+#include <phal_exception.H>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <phosphor-logging/log.hpp>
+
+#include <system_error>
+#include <vector>
+
+namespace openpower
+{
+namespace misc
+{
+
+/**
+ * @brief Calls sbe_enter_mpipl on the SBE in the provided target.
+ * @return void
+ */
+void sbeEnterMpReboot(struct pdbg_target* tgt)
+{
+    using namespace openpower::pel;
+    using namespace openpower::phal;
+    using namespace openpower::phal::sbe;
+    using namespace openpower::phal::exception;
+    using namespace phosphor::logging;
+
+    try
+    {
+        mpiplEnter(tgt);
+    }
+    catch (const sbeError_t& sbeError)
+    {
+        log<level::ERR>(fmt::format("EnterMPIPL failed({}) on proc({})",
+                                    sbeError.what(), pdbg_target_index(tgt))
+                            .c_str());
+
+        std::string event;
+        bool dumpIsRequired = false;
+
+        if (sbeError.errType() == SBE_CMD_TIMEOUT)
+        {
+            event = "org.open_power.Processor.Error.SbeChipOpTimeout";
+            dumpIsRequired = true;
+        }
+        else
+        {
+            event = "org.open_power.Processor.Error.SbeChipOpFailure";
+        }
+
+        // SRC6 : [0:15] chip position [16:23] command class, [24:31] Type
+        uint32_t index = pdbg_target_index(tgt);
+
+        // TODO Replace these consts with pdbg defines once it is exported.
+        // Ref : pdbg/libsbefifo/sbefifo_private.h
+        constexpr auto SBEFIFO_CMD_CLASS_MPIPL = 0xA900;
+        constexpr auto SBEFIFO_CMD_ENTER_MPIPL = 0x01;
+        uint32_t cmd = SBEFIFO_CMD_CLASS_MPIPL | SBEFIFO_CMD_ENTER_MPIPL;
+
+        // To store additional data about ffdc.
+        FFDCData pelAdditionalData;
+        pelAdditionalData.emplace_back("SRC6",
+                                       std::to_string((index << 16) | cmd));
+        createSbeErrorPEL(event, sbeError, pelAdditionalData);
+
+        if (dumpIsRequired)
+        {
+            // TODO Request SBE Dump
+        }
+        throw;
+    }
+    // Capture genaral libphal error
+    catch (const phalError_t& phalError)
+    {
+        // Failure reported
+        log<level::ERR>(fmt::format("captureFFDC: Exception({}) on proc({})",
+                                    phalError.what(), pdbg_target_index(tgt))
+                            .c_str());
+        openpower::pel::createPEL(
+            "org.open_power.Processor.Error.SbeChipOpFailure");
+        throw;
+    }
+
+    log<level::INFO>(
+        fmt::format("Enter MPIPL completed on proc({})", pdbg_target_index(tgt))
+            .c_str());
+}
+
+/**
+ * @brief initiate memory preserving reboot on each SBE.
+ * @return void
+ */
+void enterMpReboot()
+{
+    using namespace phosphor::logging;
+    struct pdbg_target* target;
+    std::vector<pid_t> pidList;
+    bool failed = false;
+    pdbg_targets_init(NULL);
+    ATTR_HWAS_STATE_Type hwasState;
+
+    log<level::INFO>("Starting memory preserving reboot");
+    pdbg_for_each_class_target("proc", target)
+    {
+        if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
+        {
+            log<level::ERR>("Could not read HWAS_STATE attribute");
+        }
+        if (!hwasState.functional)
+        {
+            continue;
+        }
+
+        pid_t pid = fork();
+
+        if (pid < 0)
+        {
+            log<level::ERR>("Fork failed while starting mp reboot");
+            failed = true;
+        }
+        else if (pid == 0)
+        {
+            sbeEnterMpReboot(target);
+            std::exit(EXIT_SUCCESS);
+        }
+        else
+        {
+            pidList.push_back(std::move(pid));
+        }
+    }
+
+    for (auto& p : pidList)
+    {
+        int status = 0;
+        waitpid(p, &status, 0);
+        if (WEXITSTATUS(status))
+        {
+            log<level::ERR>("Memory preserving reboot failed");
+            failed = true;
+        }
+    }
+
+    if (failed)
+    {
+        std::exit(EXIT_FAILURE);
+    }
+}
+
+REGISTER_PROCEDURE("enterMpReboot", enterMpReboot)
+
+} // namespace misc
+} // namespace openpower