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