xref: /openbmc/openpower-proc-control/procedures/phal/enter_mpreboot.cpp (revision 6aba83d21d9fe55ee0dca3f829de44824cc16c28)
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         log<level::ERR>(fmt::format("EnterMPIPL failed({}) on proc({})",
64                                     sbeError.what(), pdbg_target_index(tgt))
65                             .c_str());
66 
67         std::string event;
68         bool dumpIsRequired = false;
69 
70         if (sbeError.errType() == SBE_CMD_TIMEOUT)
71         {
72             event = "org.open_power.Processor.Error.SbeChipOpTimeout";
73             dumpIsRequired = true;
74         }
75         else
76         {
77             event = "org.open_power.Processor.Error.SbeChipOpFailure";
78         }
79 
80         // SRC6 : [0:15] chip position [16:23] command class, [24:31] Type
81         uint32_t index = pdbg_target_index(tgt);
82 
83         // TODO Replace these consts with pdbg defines once it is exported.
84         // Ref : pdbg/libsbefifo/sbefifo_private.h
85         constexpr auto SBEFIFO_CMD_CLASS_MPIPL = 0xA900;
86         constexpr auto SBEFIFO_CMD_ENTER_MPIPL = 0x01;
87         uint32_t cmd = SBEFIFO_CMD_CLASS_MPIPL | SBEFIFO_CMD_ENTER_MPIPL;
88 
89         // To store additional data about ffdc.
90         FFDCData pelAdditionalData;
91         pelAdditionalData.emplace_back("SRC6",
92                                        std::to_string((index << 16) | cmd));
93         auto logId = createSbeErrorPEL(event, sbeError, pelAdditionalData);
94 
95         if (dumpIsRequired)
96         {
97             // Request SBE Dump
98             using namespace openpower::phal::dump;
99             DumpParameters dumpParameters = {logId, index, SBE_DUMP_TIMEOUT,
100                                              DumpType::SBE};
101             requestDump(dumpParameters);
102         }
103         throw;
104     }
105     // Capture genaral libphal error
106     catch (const phalError_t& phalError)
107     {
108         // Failure reported
109         log<level::ERR>(fmt::format("captureFFDC: Exception({}) on proc({})",
110                                     phalError.what(), pdbg_target_index(tgt))
111                             .c_str());
112         openpower::pel::createPEL(
113             "org.open_power.Processor.Error.SbeChipOpFailure");
114         throw;
115     }
116 
117     log<level::INFO>(
118         fmt::format("Enter MPIPL completed on proc({})", pdbg_target_index(tgt))
119             .c_str());
120 }
121 
122 /**
123  * @brief initiate memory preserving reboot on each SBE.
124  * @return void
125  */
126 void enterMpReboot()
127 {
128     using namespace phosphor::logging;
129     struct pdbg_target* target;
130     std::vector<pid_t> pidList;
131     bool failed = false;
132     pdbg_targets_init(NULL);
133     ATTR_HWAS_STATE_Type hwasState;
134 
135     log<level::INFO>("Starting memory preserving reboot");
136     pdbg_for_each_class_target("proc", target)
137     {
138         if (DT_GET_PROP(ATTR_HWAS_STATE, target, hwasState))
139         {
140             log<level::ERR>("Could not read HWAS_STATE attribute");
141         }
142         if (!hwasState.functional)
143         {
144             continue;
145         }
146 
147         pid_t pid = fork();
148 
149         if (pid < 0)
150         {
151             log<level::ERR>("Fork failed while starting mp reboot");
152             failed = true;
153         }
154         else if (pid == 0)
155         {
156             sbeEnterMpReboot(target);
157             std::exit(EXIT_SUCCESS);
158         }
159         else
160         {
161             pidList.push_back(std::move(pid));
162         }
163     }
164 
165     for (auto& p : pidList)
166     {
167         int status = 0;
168         waitpid(p, &status, 0);
169         if (WEXITSTATUS(status))
170         {
171             log<level::ERR>("Memory preserving reboot failed");
172             failed = true;
173         }
174     }
175 
176     if (failed)
177     {
178         std::exit(EXIT_FAILURE);
179     }
180 }
181 
182 REGISTER_PROCEDURE("enterMpReboot", enterMpReboot)
183 
184 } // namespace misc
185 } // namespace openpower
186