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