1 extern "C"
2 {
3 #include "libpdbg.h"
4 }
5 
6 #include "extensions/phal/common_utils.hpp"
7 #include "extensions/phal/create_pel.hpp"
8 #include "extensions/phal/pdbg_utils.hpp"
9 #include "p10_cfam.hpp"
10 #include "registration.hpp"
11 
12 #include <phosphor-logging/log.hpp>
13 
14 #include <cstdio>
15 #include <fstream>
16 #include <memory>
17 
18 namespace openpower
19 {
20 namespace phal
21 {
22 
23 using namespace openpower::cfam::p10;
24 using namespace phosphor::logging;
25 
26 /**
27  * This is the backup plan to ensuring the host is not running before the
28  * BMC issues a power off to the system. Prior to this procedure being called,
29  * the BMC has tried all other communication mechanisms to talk with the host
30  * and they have failed. The design is that the host firmware will write the
31  * value 0xA5000001 to Mailbox scratch register 12 when they are up and running
32  * to a point where communication to the BMC is no longer required to function.
33  * On a power off or shutdown this register is cleared by the host and BMC
34  * firmware. If the BMC sees the 0xA5000001 pattern in the scratch register
35  * then it assumes the host is running and will leave power on to the system.
36  */
37 void checkHostRunning()
38 {
39     struct pdbg_target* procTarget;
40 
41     try
42     {
43         phal_init();
44     }
45     catch (const std::exception& ex)
46     {
47         // This should "never" happen so just throw the exception and let
48         // our systemd error handling process this
49         log<level::ERR>("Exception raised during init PHAL",
50                         entry("EXCEPTION=%s", ex.what()));
51         throw std::runtime_error("PHAL initialization failed");
52     }
53 
54     pdbg_for_each_class_target("proc", procTarget)
55     {
56         // Only check the primary proc
57         if (!isPrimaryProc(procTarget))
58         {
59             continue;
60         }
61 
62         uint32_t val = 0;
63         constexpr uint32_t HOST_RUNNING_INDICATION = 0xA5000001;
64         auto rc = getCFAM(procTarget, P10_SCRATCH_REG_12, val);
65         if ((rc == 0) && (val != HOST_RUNNING_INDICATION))
66         {
67             log<level::INFO>("CFAM read indicates host is not running",
68                              entry("CFAM=0x%X", val));
69             return;
70         }
71 
72         if (rc != 0)
73         {
74             // On error, we have to assume host is up so just fall through
75             // to code below
76             log<level::ERR>("CFAM read error, assume host is running");
77         }
78         else if (val == HOST_RUNNING_INDICATION)
79         {
80             // This is not good. Normal communication path to host did not work
81             // but CFAM indicates host is running.
82             log<level::ERR>("CFAM read indicates host is running");
83         }
84 
85         // Create an error so user knows system is in a bad state
86         openpower::pel::createPEL("org.open_power.PHAL.Error.HostRunning");
87 
88         // Create file for host instance and create in filesystem to
89         // indicate to services that host is running.
90         // This file is cleared by the phosphor-state-manager once the host
91         // start target completes.
92         constexpr auto HOST_RUNNING_FILE = "/run/openbmc/host@%d-on";
93         auto size = std::snprintf(nullptr, 0, HOST_RUNNING_FILE, 0);
94         size++; // null
95         std::unique_ptr<char[]> buf(new char[size]);
96         std::snprintf(buf.get(), size, HOST_RUNNING_FILE, 0);
97         std::ofstream outfile(buf.get());
98         outfile.close();
99         return;
100     }
101 
102     // We should "never" make it here. If we did it implies no primary processor
103     // was found. Once again, rely on systemd recovery if this happens
104     log<level::ERR>("No primary processor found in checkHostRunning");
105     throw std::runtime_error("No primary processor found in checkHostRunning");
106 }
107 
108 /**
109  * The BMC is to make a best effort to clear the CFAM register used by PHYP
110  * to indicate it is running when the host is stopped. This procedure will do
111  * that.
112  */
113 void clearHostRunning()
114 {
115     struct pdbg_target* procTarget;
116     log<level::INFO>("Entering clearHostRunning");
117 
118     try
119     {
120         phal_init();
121     }
122     catch (const std::exception& ex)
123     {
124         // This should "never" happen so just throw the exception and let
125         // our systemd error handling process this
126         log<level::ERR>("Exception raised during init PHAL",
127                         entry("EXCEPTION=%s", ex.what()));
128         throw std::runtime_error("PHAL initialization failed");
129     }
130 
131     pdbg_for_each_class_target("proc", procTarget)
132     {
133         // Only check the primary proc
134         if (!isPrimaryProc(procTarget))
135         {
136             continue;
137         }
138 
139         constexpr uint32_t HOST_NOT_RUNNING_INDICATION = 0;
140         auto rc = putCFAM(procTarget, P10_SCRATCH_REG_12,
141                           HOST_NOT_RUNNING_INDICATION);
142         if (rc != 0)
143         {
144             log<level::ERR>("CFAM write to clear host running status failed");
145         }
146 
147         // It's best effort, so just return either way
148         return;
149     }
150     log<level::ERR>("No primary processor found in clearHostRunning");
151 }
152 
153 REGISTER_PROCEDURE("checkHostRunning", checkHostRunning)
154 REGISTER_PROCEDURE("clearHostRunning", clearHostRunning)
155 
156 } // namespace phal
157 } // namespace openpower
158