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