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