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 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 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 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 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 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 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 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 302 uint8_t RedundancyPriority::sdbusPriority(uint8_t value) 303 { 304 parent.parent.savePriority(parent.versionId, value); 305 return softwareServer::RedundancyPriority::priority(value); 306 } 307 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 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 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 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 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 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 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 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