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 <phosphor-logging/elog-errors.hpp> 9 #include <phosphor-logging/elog.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <sdbusplus/exception.hpp> 12 #include <xyz/openbmc_project/Common/error.hpp> 13 #include <xyz/openbmc_project/Software/Version/error.hpp> 14 15 #ifdef WANT_SIGNATURE_VERIFY 16 #include "image_verify.hpp" 17 #endif 18 19 namespace phosphor 20 { 21 namespace software 22 { 23 namespace updater 24 { 25 26 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; 27 28 using namespace phosphor::logging; 29 using sdbusplus::exception::SdBusError; 30 using InternalFailure = 31 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 32 33 #ifdef WANT_SIGNATURE_VERIFY 34 namespace control = sdbusplus::xyz::openbmc_project::Control::server; 35 #endif 36 37 void Activation::subscribeToSystemdSignals() 38 { 39 auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 40 SYSTEMD_INTERFACE, "Subscribe"); 41 try 42 { 43 this->bus.call_noreply(method); 44 } 45 catch (const SdBusError& e) 46 { 47 if (e.name() != nullptr && 48 strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0) 49 { 50 // If an Activation attempt fails, the Unsubscribe method is not 51 // called. This may lead to an AlreadySubscribed error if the 52 // Activation is re-attempted. 53 } 54 else 55 { 56 log<level::ERR>("Error subscribing to systemd", 57 entry("ERROR=%s", e.what())); 58 } 59 } 60 61 return; 62 } 63 64 void Activation::unsubscribeFromSystemdSignals() 65 { 66 auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 67 SYSTEMD_INTERFACE, "Unsubscribe"); 68 try 69 { 70 this->bus.call_noreply(method); 71 } 72 catch (const SdBusError& e) 73 { 74 log<level::ERR>("Error in unsubscribing from systemd signals", 75 entry("ERROR=%s", e.what())); 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 92 #ifdef HOST_BIOS_UPGRADE 93 auto purpose = parent.versions.find(versionId)->second->purpose(); 94 if (purpose == VersionPurpose::Host) 95 { 96 if (!activationProgress) 97 { 98 activationProgress = 99 std::make_unique<ActivationProgress>(bus, path); 100 } 101 102 // Enable systemd signals 103 subscribeToSystemdSignals(); 104 105 // Set initial progress 106 activationProgress->progress(20); 107 108 // Initiate image writing to flash 109 flashWriteHost(); 110 111 return softwareServer::Activation::activation(value); 112 } 113 #endif 114 115 auto versionStr = parent.versions.find(versionId)->second->version(); 116 117 if (!minimum_ship_level::verify(versionStr)) 118 { 119 using namespace phosphor::logging; 120 using IncompatibleErr = sdbusplus::xyz::openbmc_project::Software:: 121 Version::Error::Incompatible; 122 using Incompatible = 123 xyz::openbmc_project::Software::Version::Incompatible; 124 125 report<IncompatibleErr>( 126 prev_entry<Incompatible::MIN_VERSION>(), 127 prev_entry<Incompatible::ACTUAL_VERSION>(), 128 prev_entry<Incompatible::VERSION_PURPOSE>()); 129 return softwareServer::Activation::activation( 130 softwareServer::Activation::Activations::Failed); 131 } 132 133 #ifdef WANT_SIGNATURE_VERIFY 134 fs::path uploadDir(IMG_UPLOAD_DIR); 135 if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) 136 { 137 onVerifyFailed(); 138 // Stop the activation process, if fieldMode is enabled. 139 if (parent.control::FieldMode::fieldModeEnabled()) 140 { 141 return softwareServer::Activation::activation( 142 softwareServer::Activation::Activations::Failed); 143 } 144 } 145 #endif 146 147 if (!activationProgress) 148 { 149 activationProgress = 150 std::make_unique<ActivationProgress>(bus, path); 151 } 152 153 if (!activationBlocksTransition) 154 { 155 activationBlocksTransition = 156 std::make_unique<ActivationBlocksTransition>(bus, path); 157 } 158 159 activationProgress->progress(10); 160 161 parent.freeSpace(*this); 162 163 // Enable systemd signals 164 Activation::subscribeToSystemdSignals(); 165 166 flashWrite(); 167 168 #if defined UBIFS_LAYOUT || defined MMC_LAYOUT 169 170 return softwareServer::Activation::activation(value); 171 172 #else // STATIC_LAYOUT 173 174 onFlashWriteSuccess(); 175 return softwareServer::Activation::activation( 176 softwareServer::Activation::Activations::Active); 177 #endif 178 } 179 else 180 { 181 activationBlocksTransition.reset(nullptr); 182 activationProgress.reset(nullptr); 183 } 184 return softwareServer::Activation::activation(value); 185 } 186 187 void Activation::onFlashWriteSuccess() 188 { 189 activationProgress->progress(100); 190 191 activationBlocksTransition.reset(nullptr); 192 activationProgress.reset(nullptr); 193 194 rwVolumeCreated = false; 195 roVolumeCreated = false; 196 ubootEnvVarsUpdated = false; 197 Activation::unsubscribeFromSystemdSignals(); 198 199 storePurpose(versionId, parent.versions.find(versionId)->second->purpose()); 200 201 if (!redundancyPriority) 202 { 203 redundancyPriority = 204 std::make_unique<RedundancyPriority>(bus, path, *this, 0); 205 } 206 207 // Remove version object from image manager 208 Activation::deleteImageManagerObject(); 209 210 // Create active association 211 parent.createActiveAssociation(path); 212 213 // Create updateable association as this 214 // can be re-programmed. 215 parent.createUpdateableAssociation(path); 216 217 if (Activation::checkApplyTimeImmediate() == true) 218 { 219 log<level::INFO>("Image Active. ApplyTime is immediate, " 220 "rebooting BMC."); 221 Activation::rebootBmc(); 222 } 223 else 224 { 225 log<level::INFO>("BMC image ready, need reboot to get activated."); 226 } 227 228 activation(softwareServer::Activation::Activations::Active); 229 } 230 231 void Activation::deleteImageManagerObject() 232 { 233 // Call the Delete object for <versionID> inside image_manager 234 auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(), 235 "xyz.openbmc_project.Object.Delete", 236 "Delete"); 237 try 238 { 239 bus.call_noreply(method); 240 } 241 catch (const SdBusError& e) 242 { 243 log<level::ERR>("Error in Deleting image from image manager", 244 entry("VERSIONPATH=%s", path.c_str())); 245 return; 246 } 247 } 248 249 auto Activation::requestedActivation(RequestedActivations value) 250 -> RequestedActivations 251 { 252 rwVolumeCreated = false; 253 roVolumeCreated = false; 254 ubootEnvVarsUpdated = false; 255 256 if ((value == softwareServer::Activation::RequestedActivations::Active) && 257 (softwareServer::Activation::requestedActivation() != 258 softwareServer::Activation::RequestedActivations::Active)) 259 { 260 if ((softwareServer::Activation::activation() == 261 softwareServer::Activation::Activations::Ready) || 262 (softwareServer::Activation::activation() == 263 softwareServer::Activation::Activations::Failed)) 264 { 265 Activation::activation( 266 softwareServer::Activation::Activations::Activating); 267 } 268 } 269 return softwareServer::Activation::requestedActivation(value); 270 } 271 272 uint8_t RedundancyPriority::priority(uint8_t value) 273 { 274 // Set the priority value so that the freePriority() function can order 275 // the versions by priority. 276 auto newPriority = softwareServer::RedundancyPriority::priority(value); 277 parent.parent.savePriority(parent.versionId, value); 278 parent.parent.freePriority(value, parent.versionId); 279 return newPriority; 280 } 281 282 uint8_t RedundancyPriority::sdbusPriority(uint8_t value) 283 { 284 parent.parent.savePriority(parent.versionId, value); 285 return softwareServer::RedundancyPriority::priority(value); 286 } 287 288 void Activation::unitStateChange(sdbusplus::message::message& msg) 289 { 290 if (softwareServer::Activation::activation() != 291 softwareServer::Activation::Activations::Activating) 292 { 293 return; 294 } 295 296 #ifdef HOST_BIOS_UPGRADE 297 auto purpose = parent.versions.find(versionId)->second->purpose(); 298 if (purpose == VersionPurpose::Host) 299 { 300 onStateChangesBios(msg); 301 return; 302 } 303 #endif 304 305 onStateChanges(msg); 306 307 return; 308 } 309 310 #ifdef WANT_SIGNATURE_VERIFY 311 bool Activation::verifySignature(const fs::path& imageDir, 312 const fs::path& confDir) 313 { 314 using Signature = phosphor::software::image::Signature; 315 316 Signature signature(imageDir, confDir); 317 318 return signature.verify(); 319 } 320 321 void Activation::onVerifyFailed() 322 { 323 log<level::ERR>("Error occurred during image validation"); 324 report<InternalFailure>(); 325 } 326 #endif 327 328 void ActivationBlocksTransition::enableRebootGuard() 329 { 330 log<level::INFO>("BMC image activating - BMC reboots are disabled."); 331 332 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 333 SYSTEMD_INTERFACE, "StartUnit"); 334 method.append("reboot-guard-enable.service", "replace"); 335 bus.call_noreply(method); 336 } 337 338 void ActivationBlocksTransition::disableRebootGuard() 339 { 340 log<level::INFO>("BMC activation has ended - BMC reboots are re-enabled."); 341 342 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 343 SYSTEMD_INTERFACE, "StartUnit"); 344 method.append("reboot-guard-disable.service", "replace"); 345 bus.call_noreply(method); 346 } 347 348 bool Activation::checkApplyTimeImmediate() 349 { 350 auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf); 351 if (service.empty()) 352 { 353 log<level::INFO>("Error getting the service name for BMC image " 354 "ApplyTime. The BMC needs to be manually rebooted to " 355 "complete the image activation if needed " 356 "immediately."); 357 } 358 else 359 { 360 361 auto method = bus.new_method_call(service.c_str(), applyTimeObjPath, 362 dbusPropIntf, "Get"); 363 method.append(applyTimeIntf, applyTimeProp); 364 365 try 366 { 367 auto reply = bus.call(method); 368 369 std::variant<std::string> result; 370 reply.read(result); 371 auto applyTime = std::get<std::string>(result); 372 if (applyTime == applyTimeImmediate) 373 { 374 return true; 375 } 376 } 377 catch (const SdBusError& e) 378 { 379 log<level::ERR>("Error in getting ApplyTime", 380 entry("ERROR=%s", e.what())); 381 } 382 } 383 return false; 384 } 385 386 #ifdef HOST_BIOS_UPGRADE 387 void Activation::flashWriteHost() 388 { 389 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 390 SYSTEMD_INTERFACE, "StartUnit"); 391 auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; 392 method.append(biosServiceFile, "replace"); 393 try 394 { 395 auto reply = bus.call(method); 396 } 397 catch (const SdBusError& e) 398 { 399 log<level::ERR>("Error in trying to upgrade Host Bios."); 400 report<InternalFailure>(); 401 } 402 } 403 404 void Activation::onStateChangesBios(sdbusplus::message::message& msg) 405 { 406 uint32_t newStateID{}; 407 sdbusplus::message::object_path newStateObjPath; 408 std::string newStateUnit{}; 409 std::string newStateResult{}; 410 411 // Read the msg and populate each variable 412 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 413 414 auto biosServiceFile = "obmc-flash-host-bios@" + versionId + ".service"; 415 416 if (newStateUnit == biosServiceFile) 417 { 418 // unsubscribe to systemd signals 419 unsubscribeFromSystemdSignals(); 420 421 // Remove version object from image manager 422 deleteImageManagerObject(); 423 424 if (newStateResult == "done") 425 { 426 // Set activation progress to 100 427 activationProgress->progress(100); 428 429 // Set Activation value to active 430 activation(softwareServer::Activation::Activations::Active); 431 432 log<level::INFO>("Bios upgrade completed successfully."); 433 } 434 else if (newStateResult == "failed") 435 { 436 // Set Activation value to Failed 437 activation(softwareServer::Activation::Activations::Failed); 438 439 log<level::ERR>("Bios upgrade failed."); 440 } 441 } 442 443 return; 444 } 445 446 #endif 447 448 void Activation::rebootBmc() 449 { 450 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 451 SYSTEMD_INTERFACE, "StartUnit"); 452 method.append("force-reboot.service", "replace"); 453 try 454 { 455 auto reply = bus.call(method); 456 } 457 catch (const SdBusError& e) 458 { 459 log<level::ALERT>("Error in trying to reboot the BMC. " 460 "The BMC needs to be manually rebooted to complete " 461 "the image activation."); 462 report<InternalFailure>(); 463 } 464 } 465 466 } // namespace updater 467 } // namespace software 468 } // namespace phosphor 469