1 #include "activation.hpp"
2 #include "images.hpp"
3 #include "item_updater.hpp"
4 #include "serialize.hpp"
5 #include <phosphor-logging/log.hpp>
6 #include <sdbusplus/exception.hpp>
7 
8 #ifdef WANT_SIGNATURE_VERIFY
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 #include "image_verify.hpp"
13 #endif
14 
15 namespace phosphor
16 {
17 namespace software
18 {
19 namespace updater
20 {
21 
22 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
23 
24 using namespace phosphor::logging;
25 using sdbusplus::exception::SdBusError;
26 
27 #ifdef WANT_SIGNATURE_VERIFY
28 using InternalFailure =
29     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
30 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
31 #endif
32 
33 void Activation::subscribeToSystemdSignals()
34 {
35     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
36                                             SYSTEMD_INTERFACE, "Subscribe");
37     try
38     {
39         this->bus.call_noreply(method);
40     }
41     catch (const SdBusError& e)
42     {
43         if (e.name() != nullptr &&
44             strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
45         {
46             // If an Activation attempt fails, the Unsubscribe method is not
47             // called. This may lead to an AlreadySubscribed error if the
48             // Activation is re-attempted.
49         }
50         else
51         {
52             log<level::ERR>("Error subscribing to systemd",
53                             entry("ERROR=%s", e.what()));
54         }
55     }
56 
57     return;
58 }
59 
60 void Activation::unsubscribeFromSystemdSignals()
61 {
62     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
63                                             SYSTEMD_INTERFACE, "Unsubscribe");
64     this->bus.call_noreply(method);
65 
66     return;
67 }
68 
69 auto Activation::activation(Activations value) -> Activations
70 {
71 
72     if ((value != softwareServer::Activation::Activations::Active) &&
73         (value != softwareServer::Activation::Activations::Activating))
74     {
75         redundancyPriority.reset(nullptr);
76     }
77 
78     if (value == softwareServer::Activation::Activations::Activating)
79     {
80 #ifdef UBIFS_LAYOUT
81         if (rwVolumeCreated == false && roVolumeCreated == false)
82         {
83             // Enable systemd signals
84             Activation::subscribeToSystemdSignals();
85 
86             parent.freeSpace();
87 
88             if (!activationProgress)
89             {
90                 activationProgress =
91                     std::make_unique<ActivationProgress>(bus, path);
92             }
93 
94             if (!activationBlocksTransition)
95             {
96                 activationBlocksTransition =
97                     std::make_unique<ActivationBlocksTransition>(bus, path);
98             }
99 
100 #ifdef WANT_SIGNATURE_VERIFY
101             fs::path uploadDir(IMG_UPLOAD_DIR);
102             if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
103             {
104                 onVerifyFailed();
105                 // Stop the activation process, if fieldMode is enabled.
106                 if (parent.control::FieldMode::fieldModeEnabled())
107                 {
108                     // Cleanup
109                     activationBlocksTransition.reset(nullptr);
110                     activationProgress.reset(nullptr);
111                     return softwareServer::Activation::activation(
112                         softwareServer::Activation::Activations::Failed);
113                 }
114             }
115 #endif
116 
117             flashWrite();
118 
119             activationProgress->progress(10);
120         }
121         else if (rwVolumeCreated == true && roVolumeCreated == true)
122         {
123             if (ubootEnvVarsUpdated == false)
124             {
125                 activationProgress->progress(90);
126 
127                 if (!redundancyPriority)
128                 {
129                     redundancyPriority = std::make_unique<RedundancyPriority>(
130                         bus, path, *this, 0);
131                 }
132             }
133             else
134             {
135                 activationProgress->progress(100);
136 
137                 activationBlocksTransition.reset(nullptr);
138                 activationProgress.reset(nullptr);
139 
140                 rwVolumeCreated = false;
141                 roVolumeCreated = false;
142                 ubootEnvVarsUpdated = false;
143                 Activation::unsubscribeFromSystemdSignals();
144 
145                 // Remove version object from image manager
146                 Activation::deleteImageManagerObject();
147 
148                 // Create active association
149                 parent.createActiveAssociation(path);
150 
151                 return softwareServer::Activation::activation(
152                     softwareServer::Activation::Activations::Active);
153             }
154         }
155 #else // !UBIFS_LAYOUT
156 
157 #ifdef WANT_SIGNATURE_VERIFY
158         fs::path uploadDir(IMG_UPLOAD_DIR);
159         if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
160         {
161             onVerifyFailed();
162             // Stop the activation process, if fieldMode is enabled.
163             if (parent.control::FieldMode::fieldModeEnabled())
164             {
165                 return softwareServer::Activation::activation(
166                     softwareServer::Activation::Activations::Failed);
167             }
168         }
169 #endif
170         parent.freeSpace();
171 
172         flashWrite();
173 
174         if (!redundancyPriority)
175         {
176             redundancyPriority =
177                 std::make_unique<RedundancyPriority>(bus, path, *this, 0);
178         }
179 
180         // Remove version object from image manager
181         Activation::deleteImageManagerObject();
182 
183         // Create active association
184         parent.createActiveAssociation(path);
185 
186         log<level::INFO>("BMC image ready, need reboot to get activated.");
187         return softwareServer::Activation::activation(
188             softwareServer::Activation::Activations::Active);
189 #endif
190     }
191     else
192     {
193         activationBlocksTransition.reset(nullptr);
194         activationProgress.reset(nullptr);
195     }
196     return softwareServer::Activation::activation(value);
197 }
198 
199 void Activation::deleteImageManagerObject()
200 {
201     // Call the Delete object for <versionID> inside image_manager
202     auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(),
203                                             "xyz.openbmc_project.Object.Delete",
204                                             "Delete");
205     auto mapperResponseMsg = bus.call(method);
206     // Check that the bus call didn't result in an error
207     if (mapperResponseMsg.is_method_error())
208     {
209         log<level::ERR>("Error in Deleting image from image manager",
210                         entry("VERSIONPATH=%s", path.c_str()));
211         return;
212     }
213 }
214 
215 auto Activation::requestedActivation(RequestedActivations value)
216     -> RequestedActivations
217 {
218     rwVolumeCreated = false;
219     roVolumeCreated = false;
220     ubootEnvVarsUpdated = false;
221 
222     if ((value == softwareServer::Activation::RequestedActivations::Active) &&
223         (softwareServer::Activation::requestedActivation() !=
224          softwareServer::Activation::RequestedActivations::Active))
225     {
226         if ((softwareServer::Activation::activation() ==
227              softwareServer::Activation::Activations::Ready) ||
228             (softwareServer::Activation::activation() ==
229              softwareServer::Activation::Activations::Failed))
230         {
231             Activation::activation(
232                 softwareServer::Activation::Activations::Activating);
233         }
234     }
235     return softwareServer::Activation::requestedActivation(value);
236 }
237 
238 uint8_t RedundancyPriority::priority(uint8_t value)
239 {
240     // Set the priority value so that the freePriority() function can order
241     // the versions by priority.
242     auto newPriority = softwareServer::RedundancyPriority::priority(value);
243     parent.parent.savePriority(parent.versionId, value);
244     parent.parent.freePriority(value, parent.versionId);
245     return newPriority;
246 }
247 
248 uint8_t RedundancyPriority::sdbusPriority(uint8_t value)
249 {
250     parent.parent.savePriority(parent.versionId, value);
251     return softwareServer::RedundancyPriority::priority(value);
252 }
253 
254 void Activation::unitStateChange(sdbusplus::message::message& msg)
255 {
256     if (softwareServer::Activation::activation() !=
257         softwareServer::Activation::Activations::Activating)
258     {
259         return;
260     }
261 
262     onStateChanges(msg);
263 
264     return;
265 }
266 
267 #ifdef WANT_SIGNATURE_VERIFY
268 bool Activation::verifySignature(const fs::path& imageDir,
269                                  const fs::path& confDir)
270 {
271     using Signature = phosphor::software::image::Signature;
272 
273     Signature signature(imageDir, confDir);
274 
275     return signature.verify();
276 }
277 
278 void Activation::onVerifyFailed()
279 {
280     log<level::ERR>("Error occurred during image validation");
281     report<InternalFailure>();
282 }
283 #endif
284 
285 void ActivationBlocksTransition::enableRebootGuard()
286 {
287     log<level::INFO>("BMC image activating - BMC reboots are disabled.");
288 
289     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
290                                       SYSTEMD_INTERFACE, "StartUnit");
291     method.append("reboot-guard-enable.service", "replace");
292     bus.call_noreply(method);
293 }
294 
295 void ActivationBlocksTransition::disableRebootGuard()
296 {
297     log<level::INFO>("BMC activation has ended - BMC reboots are re-enabled.");
298 
299     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
300                                       SYSTEMD_INTERFACE, "StartUnit");
301     method.append("reboot-guard-disable.service", "replace");
302     bus.call_noreply(method);
303 }
304 
305 } // namespace updater
306 } // namespace software
307 } // namespace phosphor
308