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