bmc-reset: check if host is running
This is the backup plan to ensuring the host is not running before the
BMC issues a power off to the system. Prior to this procedure being
called, the BMC has tried all other communication mechanisms to talk
with the host and they have failed. The design is that the host
firmware will write the value 0xA5000001 to Mailbox scratch register
12 when they are up and running to a point where communication to the
BMC is no longer required to function. On a power off or shutdown
this register is cleared by the host and BMC firmware. If the BMC sees
the 0xA5000001 pattern in the scratch register then it assumes the host
is running and will leave power on to the system.
Why not put this in phosphor-state-manager where the other checks are
for the host running?
- This is a very POWER10 specific piece of logic
- A lot of tight coupling would have been required between state-manager
and proc-control
- In the end, it was less work to just put this in proc-control
Tested:
- Verified when CFAM is not A5000001, call returns without error and
/run file is not created
- Verified when CFAM is A5000001, call returns without error and /run
file is created
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
Change-Id: I3f6be9160d8d89e556a722d9f6cb00f94be2b994
diff --git a/meson.build b/meson.build
index 6cf1833..47760aa 100644
--- a/meson.build
+++ b/meson.build
@@ -66,6 +66,7 @@
'procedures/phal/set_SPI_mux.cpp',
'procedures/phal/proc_pre_poweroff.cpp',
'procedures/phal/common_utils.cpp',
+ 'procedures/phal/check_host_running.cpp',
'phalerror/create_pel.cpp',
'phalerror/phal_error.cpp',
]
diff --git a/p10_cfam.hpp b/p10_cfam.hpp
index a8e47c4..f4882dd 100644
--- a/p10_cfam.hpp
+++ b/p10_cfam.hpp
@@ -8,6 +8,7 @@
{
static constexpr uint16_t P10_ROOT_CTRL8 = 0x2818;
+static constexpr uint16_t P10_SCRATCH_REG_12 = 0x2983;
} // namespace p10
} // namespace cfam
diff --git a/procedures/phal/check_host_running.cpp b/procedures/phal/check_host_running.cpp
new file mode 100644
index 0000000..a72a812
--- /dev/null
+++ b/procedures/phal/check_host_running.cpp
@@ -0,0 +1,109 @@
+extern "C"
+{
+#include "libpdbg.h"
+}
+
+#include "common_utils.hpp"
+#include "p10_cfam.hpp"
+#include "procedures/phal/common_utils.hpp"
+#include "registration.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+#include <cstdio>
+#include <fstream>
+#include <memory>
+
+namespace openpower
+{
+namespace phal
+{
+
+using namespace openpower::cfam::p10;
+using namespace phosphor::logging;
+
+/**
+ * This is the backup plan to ensuring the host is not running before the
+ * BMC issues a power off to the system. Prior to this procedure being called,
+ * the BMC has tried all other communication mechanisms to talk with the host
+ * and they have failed. The design is that the host firmware will write the
+ * value 0xA5000001 to Mailbox scratch register 12 when they are up and running
+ * to a point where communication to the BMC is no longer required to function.
+ * On a power off or shutdown this register is cleared by the host and BMC
+ * firmware. If the BMC sees the 0xA5000001 pattern in the scratch register
+ * then it assumes the host is running and will leave power on to the system.
+ */
+void checkHostRunning()
+{
+ struct pdbg_target* procTarget;
+
+ try
+ {
+ phal_init();
+ }
+ catch (std::exception& ex)
+ {
+ // This should "never" happen so just throw the exception and let
+ // our systemd error handling process this
+ log<level::ERR>("Exception raised during init PHAL",
+ entry("EXCEPTION=%s", ex.what()));
+ throw std::runtime_error("PHAL initialization failed");
+ }
+
+ pdbg_for_each_class_target("proc", procTarget)
+ {
+ // Only check the primary proc
+ if (!isPrimaryProc(procTarget))
+ {
+ continue;
+ }
+
+ uint32_t val = 0;
+ constexpr uint32_t HOST_RUNNING_INDICATION = 0xA5000001;
+ auto rc = getCFAM(procTarget, P10_SCRATCH_REG_12, val);
+ if ((rc == 0) && (val != HOST_RUNNING_INDICATION))
+ {
+ log<level::INFO>("CFAM read indicates host is not running",
+ entry("CFAM=0x%X", val));
+ return;
+ }
+
+ if (rc != 0)
+ {
+ // On error, we have to assume host is up so just fall through
+ // to code below
+ log<level::ERR>("CFAM read error, assume host is running");
+ }
+ else if (val == HOST_RUNNING_INDICATION)
+ {
+ // This is not good. Normal communication path to host did not work
+ // but CFAM indicates host is running.
+ log<level::ERR>("CFAM read indicates host is running");
+ }
+
+ // TODO - Create Error
+
+ // Create file for host instance and create in filesystem to
+ // indicate to services that host is running.
+ // This file is cleared by the phosphor-state-manager once the host
+ // start target completes.
+ constexpr auto HOST_RUNNING_FILE = "/run/openbmc/host@%d-on";
+ auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
+ size++; // null
+ std::unique_ptr<char[]> buf(new char[size]);
+ std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
+ std::ofstream outfile(buf.get());
+ outfile.close();
+ return;
+ }
+
+ // We should "never" make it here. If we did it implies no primary processor
+ // was found. Once again, rely on systemd recovery if this happens
+ log<level::ERR>("No primary processor found in checkHostRunning");
+ throw std::runtime_error("No primary processor found in checkHostRunning");
+}
+
+REGISTER_PROCEDURE("checkHostRunning", checkHostRunning)
+
+} // namespace phal
+} // namespace openpower
diff --git a/procedures/phal/common_utils.cpp b/procedures/phal/common_utils.cpp
index ca37ace..708df40 100644
--- a/procedures/phal/common_utils.cpp
+++ b/procedures/phal/common_utils.cpp
@@ -59,11 +59,6 @@
}
}
-/**
- * @brief Check if primary processor or not
- *
- * @return True/False
- */
bool isPrimaryProc(struct pdbg_target* procTarget)
{
ATTR_PROC_MASTER_TYPE_Type type;
@@ -87,5 +82,46 @@
}
}
+uint32_t getCFAM(struct pdbg_target* procTarget, const uint32_t reg,
+ uint32_t& val)
+{
+ auto procIdx = pdbg_target_index(procTarget);
+ char path[16];
+ sprintf(path, "/proc%d/pib", procIdx);
+
+ pdbg_target* pibTarget = pdbg_target_from_path(nullptr, path);
+ if (nullptr == pibTarget)
+ {
+ log<level::ERR>("pib path of target not found",
+ entry("TARGET_PATH=%s", path));
+ return -1;
+ }
+
+ // probe PIB and ensure it's enabled
+ if (PDBG_TARGET_ENABLED != pdbg_target_probe(pibTarget))
+ {
+ log<level::ERR>("probe on pib target failed");
+ return -1;
+ }
+
+ // now build FSI path and read the input reg
+ sprintf(path, "/proc%d/fsi", procIdx);
+ pdbg_target* fsiTarget = pdbg_target_from_path(nullptr, path);
+ if (nullptr == fsiTarget)
+ {
+ log<level::ERR>("fsi path or target not found");
+ return -1;
+ }
+
+ auto rc = fsi_read(fsiTarget, reg, &val);
+ if (rc)
+ {
+ log<level::ERR>("failed to read input cfam", entry("RC=%u", rc),
+ entry("CFAM=0x%X", reg), entry("TARGET_PATH=%s", path));
+ return rc;
+ }
+ return 0;
+}
+
} // namespace phal
} // namespace openpower
diff --git a/procedures/phal/common_utils.hpp b/procedures/phal/common_utils.hpp
index 623d071..2d9d57b 100644
--- a/procedures/phal/common_utils.hpp
+++ b/procedures/phal/common_utils.hpp
@@ -25,11 +25,23 @@
/**
* @brief Check if primary processor or not
*
- * * @param[in] procTarget - Target to check if primary or not
+ * @param[in] procTarget - Target to check if primary or not
*
* @return True/False
*/
bool isPrimaryProc(struct pdbg_target* procTarget);
+/**
+ * @brief Read the input CFAM register
+ *
+ * @param[in] procTarget - The Target to perform the operation on
+ * @param[in] reg - The register address to read
+ * @param[out] val - The value read from the register
+ *
+ * @return 0 on success, non-0 on failure
+ */
+uint32_t getCFAM(struct pdbg_target* procTarget, const uint32_t reg,
+ uint32_t& val);
+
} // namespace phal
} // namespace openpower