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