1 /**
2 * Copyright (C) 2020 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "registration.hpp"
18
19 extern "C"
20 {
21 #include <libpdbg.h>
22 #include <libpdbg_sbe.h>
23 }
24
25 #include "extensions/phal/create_pel.hpp"
26 #include "extensions/phal/dump_utils.hpp"
27
28 #include <attributes_info.H>
29 #include <libphal.H>
30 #include <phal_exception.H>
31 #include <sys/wait.h>
32 #include <unistd.h>
33
34 #include <phosphor-logging/log.hpp>
35
36 #include <format>
37 #include <system_error>
38 #include <vector>
39
40 namespace openpower
41 {
42 namespace misc
43 {
44
45 /**
46 * @brief Calls sbe_enter_mpipl on the SBE in the provided target.
47 * @return void
48 */
sbeEnterMpReboot(struct pdbg_target * tgt)49 void sbeEnterMpReboot(struct pdbg_target* tgt)
50 {
51 using namespace openpower::pel;
52 using namespace openpower::phal;
53 using namespace openpower::phal::sbe;
54 using namespace openpower::phal::exception;
55 using namespace phosphor::logging;
56
57 try
58 {
59 mpiplEnter(tgt);
60 }
61 catch (const sbeError_t& sbeError)
62 {
63 if (sbeError.errType() == SBE_CHIPOP_NOT_ALLOWED)
64 {
65 // SBE is not ready to accept chip-ops,
66 // Skip the request, no additional error handling required.
67 log<level::INFO>(
68 std::format("EnterMPIPL: Skipping ({}) on proc({})",
69 sbeError.what(), pdbg_target_index(tgt))
70 .c_str());
71 return;
72 }
73
74 log<level::ERR>(std::format("EnterMPIPL failed({}) on proc({})",
75 sbeError.what(), pdbg_target_index(tgt))
76 .c_str());
77
78 std::string event;
79 bool dumpIsRequired = false;
80
81 if (sbeError.errType() == SBE_CMD_TIMEOUT)
82 {
83 event = "org.open_power.Processor.Error.SbeChipOpTimeout";
84 dumpIsRequired = true;
85 }
86 else
87 {
88 event = "org.open_power.Processor.Error.SbeChipOpFailure";
89 }
90
91 // SRC6 : [0:15] chip position [16:23] command class, [24:31] Type
92 uint32_t index = pdbg_target_index(tgt);
93
94 // TODO Replace these consts with pdbg defines once it is exported.
95 // Ref : pdbg/libsbefifo/sbefifo_private.h
96 constexpr auto SBEFIFO_CMD_CLASS_MPIPL = 0xA900;
97 constexpr auto SBEFIFO_CMD_ENTER_MPIPL = 0x01;
98 uint32_t cmd = SBEFIFO_CMD_CLASS_MPIPL | SBEFIFO_CMD_ENTER_MPIPL;
99
100 // To store additional data about ffdc.
101 FFDCData pelAdditionalData;
102 pelAdditionalData.emplace_back("SRC6",
103 std::to_string((index << 16) | cmd));
104 auto logId = createSbeErrorPEL(event, sbeError, pelAdditionalData, tgt);
105
106 if (dumpIsRequired)
107 {
108 // Request SBE Dump
109 using namespace openpower::phal::dump;
110 DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT,
111 DumpType::SBE};
112 requestDump(dumpParameters);
113 }
114 throw;
115 }
116 // Capture genaral libphal error
117 catch (const phalError_t& phalError)
118 {
119 // Failure reported
120 log<level::ERR>(std::format("captureFFDC: Exception({}) on proc({})",
121 phalError.what(), pdbg_target_index(tgt))
122 .c_str());
123 openpower::pel::createPEL(
124 "org.open_power.Processor.Error.SbeChipOpFailure");
125 throw;
126 }
127
128 log<level::INFO>(
129 std::format("Enter MPIPL completed on proc({})", pdbg_target_index(tgt))
130 .c_str());
131 }
132
133 /**
134 * @brief initiate memory preserving reboot on each SBE.
135 * @return void
136 */
enterMpReboot()137 void enterMpReboot()
138 {
139 using namespace phosphor::logging;
140 struct pdbg_target* target;
141 std::vector<pid_t> pidList;
142 bool failed = false;
143 pdbg_targets_init(NULL);
144 ATTR_HWAS_STATE_Type hwasState;
145
146 log<level::INFO>("Starting memory preserving reboot");
147 pdbg_for_each_class_target("proc", target)
148 {
149 if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
150 {
151 log<level::ERR>("Could not read HWAS_STATE attribute");
152 }
153 if (!hwasState.functional)
154 {
155 continue;
156 }
157
158 pid_t pid = fork();
159
160 if (pid < 0)
161 {
162 log<level::ERR>("Fork failed while starting mp reboot");
163 failed = true;
164 }
165 else if (pid == 0)
166 {
167 sbeEnterMpReboot(target);
168 std::exit(EXIT_SUCCESS);
169 }
170 else
171 {
172 pidList.push_back(std::move(pid));
173 }
174 }
175
176 // if no functional proc found exit with failure
177 if (pidList.size() == 0)
178 {
179 log<level::ERR>("EnterMPReboot is not executed on any processors");
180 openpower::pel::createPEL("org.open_power.PHAL.Error.MPReboot");
181 std::exit(EXIT_FAILURE);
182 }
183
184 for (auto& p : pidList)
185 {
186 int status = 0;
187 waitpid(p, &status, 0);
188 if (WEXITSTATUS(status))
189 {
190 log<level::ERR>("Memory preserving reboot failed");
191 failed = true;
192 }
193 }
194
195 if (failed)
196 {
197 std::exit(EXIT_FAILURE);
198 }
199 }
200
201 REGISTER_PROCEDURE("enterMpReboot", enterMpReboot)
202
203 } // namespace misc
204 } // namespace openpower
205