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