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