1 #include "activation.hpp"
2 
3 #include "images.hpp"
4 #include "item_updater.hpp"
5 #include "msl_verify.hpp"
6 #include "serialize.hpp"
7 
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <phosphor-logging/elog.hpp>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/exception.hpp>
12 #include <xyz/openbmc_project/Common/error.hpp>
13 #include <xyz/openbmc_project/Software/Version/error.hpp>
14 
15 #ifdef WANT_SIGNATURE_VERIFY
16 #include "image_verify.hpp"
17 #endif
18 
19 namespace phosphor
20 {
21 namespace software
22 {
23 namespace updater
24 {
25 
26 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server;
27 
28 using namespace phosphor::logging;
29 using sdbusplus::exception::SdBusError;
30 using InternalFailure =
31     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
32 
33 #ifdef WANT_SIGNATURE_VERIFY
34 namespace control = sdbusplus::xyz::openbmc_project::Control::server;
35 #endif
36 
37 void Activation::subscribeToSystemdSignals()
38 {
39     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
40                                             SYSTEMD_INTERFACE, "Subscribe");
41     try
42     {
43         this->bus.call_noreply(method);
44     }
45     catch (const SdBusError& e)
46     {
47         if (e.name() != nullptr &&
48             strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0)
49         {
50             // If an Activation attempt fails, the Unsubscribe method is not
51             // called. This may lead to an AlreadySubscribed error if the
52             // Activation is re-attempted.
53         }
54         else
55         {
56             log<level::ERR>("Error subscribing to systemd",
57                             entry("ERROR=%s", e.what()));
58         }
59     }
60 
61     return;
62 }
63 
64 void Activation::unsubscribeFromSystemdSignals()
65 {
66     auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
67                                             SYSTEMD_INTERFACE, "Unsubscribe");
68     try
69     {
70         this->bus.call_noreply(method);
71     }
72     catch (const SdBusError& e)
73     {
74         log<level::ERR>("Error in unsubscribing from systemd signals",
75                         entry("ERROR=%s", e.what()));
76     }
77 
78     return;
79 }
80 
81 auto Activation::activation(Activations value) -> Activations
82 {
83     if ((value != softwareServer::Activation::Activations::Active) &&
84         (value != softwareServer::Activation::Activations::Activating))
85     {
86         redundancyPriority.reset(nullptr);
87     }
88 
89     if (value == softwareServer::Activation::Activations::Activating)
90     {
91 
92 #ifdef HOST_BIOS_UPGRADE
93         auto purpose = parent.versions.find(versionId)->second->purpose();
94         if (purpose == VersionPurpose::Host)
95         {
96             if (!activationProgress)
97             {
98                 activationProgress =
99                     std::make_unique<ActivationProgress>(bus, path);
100             }
101 
102             // Enable systemd signals
103             subscribeToSystemdSignals();
104 
105             // Set initial progress
106             activationProgress->progress(20);
107 
108             // Initiate image writing to flash
109             flashWriteHost();
110 
111             return softwareServer::Activation::activation(value);
112         }
113 #endif
114 
115         auto versionStr = parent.versions.find(versionId)->second->version();
116 
117         if (!minimum_ship_level::verify(versionStr))
118         {
119             using namespace phosphor::logging;
120             using IncompatibleErr = sdbusplus::xyz::openbmc_project::Software::
121                 Version::Error::Incompatible;
122             using Incompatible =
123                 xyz::openbmc_project::Software::Version::Incompatible;
124 
125             report<IncompatibleErr>(
126                 prev_entry<Incompatible::MIN_VERSION>(),
127                 prev_entry<Incompatible::ACTUAL_VERSION>(),
128                 prev_entry<Incompatible::VERSION_PURPOSE>());
129             return softwareServer::Activation::activation(
130                 softwareServer::Activation::Activations::Failed);
131         }
132 
133 #ifdef WANT_SIGNATURE_VERIFY
134         fs::path uploadDir(IMG_UPLOAD_DIR);
135         if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH))
136         {
137             onVerifyFailed();
138             // Stop the activation process, if fieldMode is enabled.
139             if (parent.control::FieldMode::fieldModeEnabled())
140             {
141                 return softwareServer::Activation::activation(
142                     softwareServer::Activation::Activations::Failed);
143             }
144         }
145 #endif
146 
147         if (!activationProgress)
148         {
149             activationProgress =
150                 std::make_unique<ActivationProgress>(bus, path);
151         }
152 
153         if (!activationBlocksTransition)
154         {
155             activationBlocksTransition =
156                 std::make_unique<ActivationBlocksTransition>(bus, path);
157         }
158 
159         activationProgress->progress(10);
160 
161         parent.freeSpace(*this);
162 
163         // Enable systemd signals
164         Activation::subscribeToSystemdSignals();
165 
166         flashWrite();
167 
168 #if defined UBIFS_LAYOUT || defined MMC_LAYOUT
169 
170         return softwareServer::Activation::activation(value);
171 
172 #else // STATIC_LAYOUT
173 
174         onFlashWriteSuccess();
175         return softwareServer::Activation::activation(
176             softwareServer::Activation::Activations::Active);
177 #endif
178     }
179     else
180     {
181         activationBlocksTransition.reset(nullptr);
182         activationProgress.reset(nullptr);
183     }
184     return softwareServer::Activation::activation(value);
185 }
186 
187 void Activation::onFlashWriteSuccess()
188 {
189     activationProgress->progress(100);
190 
191     activationBlocksTransition.reset(nullptr);
192     activationProgress.reset(nullptr);
193 
194     rwVolumeCreated = false;
195     roVolumeCreated = false;
196     ubootEnvVarsUpdated = false;
197     Activation::unsubscribeFromSystemdSignals();
198 
199     storePurpose(versionId, parent.versions.find(versionId)->second->purpose());
200 
201     if (!redundancyPriority)
202     {
203         redundancyPriority =
204             std::make_unique<RedundancyPriority>(bus, path, *this, 0);
205     }
206 
207     // Remove version object from image manager
208     Activation::deleteImageManagerObject();
209 
210     // Create active association
211     parent.createActiveAssociation(path);
212 
213     // Create updateable association as this
214     // can be re-programmed.
215     parent.createUpdateableAssociation(path);
216 
217     if (Activation::checkApplyTimeImmediate() == true)
218     {
219         log<level::INFO>("Image Active. ApplyTime is immediate, "
220                          "rebooting BMC.");
221         Activation::rebootBmc();
222     }
223     else
224     {
225         log<level::INFO>("BMC image ready, need reboot to get activated.");
226     }
227 
228     activation(softwareServer::Activation::Activations::Active);
229 }
230 
231 void Activation::deleteImageManagerObject()
232 {
233     // Call the Delete object for <versionID> inside image_manager
234     auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(),
235                                             "xyz.openbmc_project.Object.Delete",
236                                             "Delete");
237     try
238     {
239         bus.call_noreply(method);
240     }
241     catch (const SdBusError& e)
242     {
243         log<level::ERR>("Error in Deleting image from image manager",
244                         entry("VERSIONPATH=%s", path.c_str()));
245         return;
246     }
247 }
248 
249 auto Activation::requestedActivation(RequestedActivations value)
250     -> RequestedActivations
251 {
252     rwVolumeCreated = false;
253     roVolumeCreated = false;
254     ubootEnvVarsUpdated = false;
255 
256     if ((value == softwareServer::Activation::RequestedActivations::Active) &&
257         (softwareServer::Activation::requestedActivation() !=
258          softwareServer::Activation::RequestedActivations::Active))
259     {
260         if ((softwareServer::Activation::activation() ==
261              softwareServer::Activation::Activations::Ready) ||
262             (softwareServer::Activation::activation() ==
263              softwareServer::Activation::Activations::Failed))
264         {
265             Activation::activation(
266                 softwareServer::Activation::Activations::Activating);
267         }
268     }
269     return softwareServer::Activation::requestedActivation(value);
270 }
271 
272 uint8_t RedundancyPriority::priority(uint8_t value)
273 {
274     // Set the priority value so that the freePriority() function can order
275     // the versions by priority.
276     auto newPriority = softwareServer::RedundancyPriority::priority(value);
277     parent.parent.savePriority(parent.versionId, value);
278     parent.parent.freePriority(value, parent.versionId);
279     return newPriority;
280 }
281 
282 uint8_t RedundancyPriority::sdbusPriority(uint8_t value)
283 {
284     parent.parent.savePriority(parent.versionId, value);
285     return softwareServer::RedundancyPriority::priority(value);
286 }
287 
288 void Activation::unitStateChange(sdbusplus::message::message& msg)
289 {
290     if (softwareServer::Activation::activation() !=
291         softwareServer::Activation::Activations::Activating)
292     {
293         return;
294     }
295 
296 #ifdef HOST_BIOS_UPGRADE
297     auto purpose = parent.versions.find(versionId)->second->purpose();
298     if (purpose == VersionPurpose::Host)
299     {
300         onStateChangesBios(msg);
301         return;
302     }
303 #endif
304 
305     onStateChanges(msg);
306 
307     return;
308 }
309 
310 #ifdef WANT_SIGNATURE_VERIFY
311 bool Activation::verifySignature(const fs::path& imageDir,
312                                  const fs::path& confDir)
313 {
314     using Signature = phosphor::software::image::Signature;
315 
316     Signature signature(imageDir, confDir);
317 
318     return signature.verify();
319 }
320 
321 void Activation::onVerifyFailed()
322 {
323     log<level::ERR>("Error occurred during image validation");
324     report<InternalFailure>();
325 }
326 #endif
327 
328 void ActivationBlocksTransition::enableRebootGuard()
329 {
330     log<level::INFO>("BMC image activating - BMC reboots are disabled.");
331 
332     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
333                                       SYSTEMD_INTERFACE, "StartUnit");
334     method.append("reboot-guard-enable.service", "replace");
335     bus.call_noreply(method);
336 }
337 
338 void ActivationBlocksTransition::disableRebootGuard()
339 {
340     log<level::INFO>("BMC activation has ended - BMC reboots are re-enabled.");
341 
342     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
343                                       SYSTEMD_INTERFACE, "StartUnit");
344     method.append("reboot-guard-disable.service", "replace");
345     bus.call_noreply(method);
346 }
347 
348 bool Activation::checkApplyTimeImmediate()
349 {
350     auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf);
351     if (service.empty())
352     {
353         log<level::INFO>("Error getting the service name for BMC image "
354                          "ApplyTime. The BMC needs to be manually rebooted to "
355                          "complete the image activation if needed "
356                          "immediately.");
357     }
358     else
359     {
360 
361         auto method = bus.new_method_call(service.c_str(), applyTimeObjPath,
362                                           dbusPropIntf, "Get");
363         method.append(applyTimeIntf, applyTimeProp);
364 
365         try
366         {
367             auto reply = bus.call(method);
368 
369             std::variant<std::string> result;
370             reply.read(result);
371             auto applyTime = std::get<std::string>(result);
372             if (applyTime == applyTimeImmediate)
373             {
374                 return true;
375             }
376         }
377         catch (const SdBusError& e)
378         {
379             log<level::ERR>("Error in getting ApplyTime",
380                             entry("ERROR=%s", e.what()));
381         }
382     }
383     return false;
384 }
385 
386 #ifdef HOST_BIOS_UPGRADE
387 void Activation::flashWriteHost()
388 {
389     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
390                                       SYSTEMD_INTERFACE, "StartUnit");
391     auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service";
392     method.append(biosServiceFile, "replace");
393     try
394     {
395         auto reply = bus.call(method);
396     }
397     catch (const SdBusError& e)
398     {
399         log<level::ERR>("Error in trying to upgrade Host Bios.");
400         report<InternalFailure>();
401     }
402 }
403 
404 void Activation::onStateChangesBios(sdbusplus::message::message& msg)
405 {
406     uint32_t newStateID{};
407     sdbusplus::message::object_path newStateObjPath;
408     std::string newStateUnit{};
409     std::string newStateResult{};
410 
411     // Read the msg and populate each variable
412     msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult);
413 
414     auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service";
415 
416     if (newStateUnit == biosServiceFile)
417     {
418         // unsubscribe to systemd signals
419         unsubscribeFromSystemdSignals();
420 
421         // Remove version object from image manager
422         deleteImageManagerObject();
423 
424         if (newStateResult == "done")
425         {
426             // Set activation progress to 100
427             activationProgress->progress(100);
428 
429             // Set Activation value to active
430             activation(softwareServer::Activation::Activations::Active);
431 
432             log<level::INFO>("Bios upgrade completed successfully.");
433         }
434         else if (newStateResult == "failed")
435         {
436             // Set Activation value to Failed
437             activation(softwareServer::Activation::Activations::Failed);
438 
439             log<level::ERR>("Bios upgrade failed.");
440         }
441     }
442 
443     return;
444 }
445 
446 #endif
447 
448 void Activation::rebootBmc()
449 {
450     auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH,
451                                       SYSTEMD_INTERFACE, "StartUnit");
452     method.append("force-reboot.service", "replace");
453     try
454     {
455         auto reply = bus.call(method);
456     }
457     catch (const SdBusError& e)
458     {
459         log<level::ALERT>("Error in trying to reboot the BMC. "
460                           "The BMC needs to be manually rebooted to complete "
461                           "the image activation.");
462         report<InternalFailure>();
463     }
464 }
465 
466 } // namespace updater
467 } // namespace software
468 } // namespace phosphor
469