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 <fmt/format.h>
30 #include <libphal.H>
31 #include <phal_exception.H>
32 #include <sys/wait.h>
33 #include <unistd.h>
34 
35 #include <phosphor-logging/log.hpp>
36 
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  */
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                 fmt::format("EnterMPIPL: Skipping ({}) on proc({})",
69                             sbeError.what(), pdbg_target_index(tgt))
70                     .c_str());
71             return;
72         }
73 
74         log<level::ERR>(fmt::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);
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>(fmt::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         fmt::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  */
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     for (auto& p : pidList)
177     {
178         int status = 0;
179         waitpid(p, &status, 0);
180         if (WEXITSTATUS(status))
181         {
182             log<level::ERR>("Memory preserving reboot failed");
183             failed = true;
184         }
185     }
186 
187     if (failed)
188     {
189         std::exit(EXIT_FAILURE);
190     }
191 }
192 
193 REGISTER_PROCEDURE("enterMpReboot", enterMpReboot)
194 
195 } // namespace misc
196 } // namespace openpower
197