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::xyz::openbmc_project::Common::Error::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::xyz::openbmc_project:: 98 Software::Version::Error::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 = std::make_unique<ActivationProgress>(bus, 120 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 = std::make_unique<RedundancyPriority>(bus, path, 196 *this, 0); 197 } 198 199 // Remove version object from image manager 200 Activation::deleteImageManagerObject(); 201 202 // Create active association 203 parent.createActiveAssociation(path); 204 205 // Create updateable association as this 206 // can be re-programmed. 207 parent.createUpdateableAssociation(path); 208 209 if (Activation::checkApplyTimeImmediate() == true) 210 { 211 info("Image Active and ApplyTime is immediate; rebooting BMC."); 212 Activation::rebootBmc(); 213 } 214 else 215 { 216 info("BMC image ready; need reboot to get activated."); 217 } 218 219 activation(softwareServer::Activation::Activations::Active); 220 } 221 222 void Activation::deleteImageManagerObject() 223 { 224 // Call the Delete object for <versionID> inside image_manager if the object 225 // has not already been deleted due to a successful update or Delete call 226 const std::string interface = std::string{VERSION_IFACE}; 227 auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 228 MAPPER_BUSNAME, "GetObject"); 229 method.append(path.c_str()); 230 method.append(std::vector<std::string>({interface})); 231 232 std::map<std::string, std::vector<std::string>> response; 233 234 try 235 { 236 auto reply = bus.call(method); 237 reply.read(response); 238 auto it = response.find(VERSION_IFACE); 239 if (it != response.end()) 240 { 241 auto deleteMethod = this->bus.new_method_call( 242 VERSION_BUSNAME, path.c_str(), 243 "xyz.openbmc_project.Object.Delete", "Delete"); 244 try 245 { 246 bus.call_noreply(deleteMethod); 247 } 248 catch (const sdbusplus::exception_t& e) 249 { 250 error( 251 "Error deleting image ({PATH}) from image manager: {ERROR}", 252 "PATH", path, "ERROR", e); 253 return; 254 } 255 } 256 } 257 catch (const sdbusplus::exception_t& e) 258 { 259 error("Error in mapper method call for ({PATH}, {INTERFACE}: {ERROR}", 260 "ERROR", e, "PATH", path, "INTERFACE", interface); 261 } 262 return; 263 } 264 265 auto Activation::requestedActivation(RequestedActivations value) 266 -> RequestedActivations 267 { 268 rwVolumeCreated = false; 269 roVolumeCreated = false; 270 ubootEnvVarsUpdated = false; 271 272 if ((value == softwareServer::Activation::RequestedActivations::Active) && 273 (softwareServer::Activation::requestedActivation() != 274 softwareServer::Activation::RequestedActivations::Active)) 275 { 276 if ((softwareServer::Activation::activation() == 277 softwareServer::Activation::Activations::Ready) || 278 (softwareServer::Activation::activation() == 279 softwareServer::Activation::Activations::Failed)) 280 { 281 Activation::activation( 282 softwareServer::Activation::Activations::Activating); 283 } 284 } 285 return softwareServer::Activation::requestedActivation(value); 286 } 287 288 uint8_t RedundancyPriority::priority(uint8_t value) 289 { 290 // Set the priority value so that the freePriority() function can order 291 // the versions by priority. 292 auto newPriority = softwareServer::RedundancyPriority::priority(value); 293 parent.parent.savePriority(parent.versionId, value); 294 parent.parent.freePriority(value, parent.versionId); 295 return newPriority; 296 } 297 298 uint8_t RedundancyPriority::sdbusPriority(uint8_t value) 299 { 300 parent.parent.savePriority(parent.versionId, value); 301 return softwareServer::RedundancyPriority::priority(value); 302 } 303 304 void Activation::unitStateChange(sdbusplus::message_t& msg) 305 { 306 if (softwareServer::Activation::activation() != 307 softwareServer::Activation::Activations::Activating) 308 { 309 return; 310 } 311 312 #ifdef HOST_BIOS_UPGRADE 313 auto purpose = parent.versions.find(versionId)->second->purpose(); 314 if (purpose == VersionPurpose::Host) 315 { 316 onStateChangesBios(msg); 317 return; 318 } 319 #endif 320 321 onStateChanges(msg); 322 323 return; 324 } 325 326 #ifdef WANT_SIGNATURE_VERIFY 327 bool Activation::verifySignature(const fs::path& imageDir, 328 const fs::path& confDir) 329 { 330 using Signature = phosphor::software::image::Signature; 331 332 Signature signature(imageDir, confDir); 333 334 return signature.verify(); 335 } 336 #endif 337 338 void ActivationBlocksTransition::enableRebootGuard() 339 { 340 info("BMC image activating - BMC reboots are disabled."); 341 342 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 343 SYSTEMD_INTERFACE, "StartUnit"); 344 method.append("reboot-guard-enable.service", "replace"); 345 bus.call_noreply(method); 346 } 347 348 void ActivationBlocksTransition::disableRebootGuard() 349 { 350 info("BMC activation has ended - BMC reboots are re-enabled."); 351 352 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 353 SYSTEMD_INTERFACE, "StartUnit"); 354 method.append("reboot-guard-disable.service", "replace"); 355 bus.call_noreply(method); 356 } 357 358 bool Activation::checkApplyTimeImmediate() 359 { 360 auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf); 361 if (service.empty()) 362 { 363 info("Error getting the service name for BMC image ApplyTime. " 364 "The BMC needs to be manually rebooted to complete the image " 365 "activation if needed immediately."); 366 } 367 else 368 { 369 auto method = bus.new_method_call(service.c_str(), applyTimeObjPath, 370 dbusPropIntf, "Get"); 371 method.append(applyTimeIntf, applyTimeProp); 372 373 try 374 { 375 auto reply = bus.call(method); 376 377 std::variant<std::string> result; 378 reply.read(result); 379 auto applyTime = std::get<std::string>(result); 380 if (applyTime == applyTimeImmediate) 381 { 382 return true; 383 } 384 } 385 catch (const sdbusplus::exception_t& e) 386 { 387 error("Error in getting ApplyTime: {ERROR}", "ERROR", e); 388 } 389 } 390 return false; 391 } 392 393 #ifdef HOST_BIOS_UPGRADE 394 void Activation::flashWriteHost() 395 { 396 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 397 SYSTEMD_INTERFACE, "StartUnit"); 398 auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; 399 method.append(biosServiceFile, "replace"); 400 try 401 { 402 auto reply = bus.call(method); 403 } 404 catch (const sdbusplus::exception_t& e) 405 { 406 error("Error in trying to upgrade Host Bios: {ERROR}", "ERROR", e); 407 report<InternalFailure>(); 408 } 409 } 410 411 void Activation::onStateChangesBios(sdbusplus::message_t& msg) 412 { 413 uint32_t newStateID{}; 414 sdbusplus::message::object_path newStateObjPath; 415 std::string newStateUnit{}; 416 std::string newStateResult{}; 417 418 // Read the msg and populate each variable 419 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 420 421 auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; 422 423 if (newStateUnit == biosServiceFile) 424 { 425 // unsubscribe to systemd signals 426 unsubscribeFromSystemdSignals(); 427 428 if (newStateResult == "done") 429 { 430 // Set activation progress to 100 431 activationProgress->progress(100); 432 433 // Set Activation value to active 434 activation(softwareServer::Activation::Activations::Active); 435 436 info("Bios upgrade completed successfully."); 437 parent.biosVersion->version( 438 parent.versions.find(versionId)->second->version()); 439 440 // Delete the uploaded activation 441 boost::asio::post(getIOContext(), [this]() { 442 this->parent.erase(this->versionId); 443 }); 444 } 445 else if (newStateResult == "failed") 446 { 447 // Set Activation value to Failed 448 activation(softwareServer::Activation::Activations::Failed); 449 450 error("Bios upgrade failed."); 451 } 452 } 453 454 return; 455 } 456 457 #endif 458 459 void Activation::rebootBmc() 460 { 461 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 462 SYSTEMD_INTERFACE, "StartUnit"); 463 method.append("force-reboot.service", "replace"); 464 try 465 { 466 auto reply = bus.call(method); 467 } 468 catch (const sdbusplus::exception_t& e) 469 { 470 alert("Error in trying to reboot the BMC. The BMC needs to be manually " 471 "rebooted to complete the image activation. {ERROR}", 472 "ERROR", e); 473 report<InternalFailure>(); 474 } 475 } 476 477 } // namespace updater 478 } // namespace software 479 } // namespace phosphor 480