1 #include "activation.hpp" 2 3 #include "images.hpp" 4 #include "item_updater.hpp" 5 #include "serialize.hpp" 6 7 #include <phosphor-logging/elog-errors.hpp> 8 #include <phosphor-logging/elog.hpp> 9 #include <phosphor-logging/log.hpp> 10 #include <sdbusplus/exception.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 13 #ifdef WANT_SIGNATURE_VERIFY 14 #include "image_verify.hpp" 15 #endif 16 17 namespace phosphor 18 { 19 namespace software 20 { 21 namespace updater 22 { 23 24 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; 25 26 using namespace phosphor::logging; 27 using sdbusplus::exception::SdBusError; 28 using InternalFailure = 29 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 30 31 #ifdef WANT_SIGNATURE_VERIFY 32 namespace control = sdbusplus::xyz::openbmc_project::Control::server; 33 #endif 34 35 void Activation::subscribeToSystemdSignals() 36 { 37 auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 38 SYSTEMD_INTERFACE, "Subscribe"); 39 try 40 { 41 this->bus.call_noreply(method); 42 } 43 catch (const SdBusError& e) 44 { 45 if (e.name() != nullptr && 46 strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0) 47 { 48 // If an Activation attempt fails, the Unsubscribe method is not 49 // called. This may lead to an AlreadySubscribed error if the 50 // Activation is re-attempted. 51 } 52 else 53 { 54 log<level::ERR>("Error subscribing to systemd", 55 entry("ERROR=%s", e.what())); 56 } 57 } 58 59 return; 60 } 61 62 void Activation::unsubscribeFromSystemdSignals() 63 { 64 auto method = this->bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 65 SYSTEMD_INTERFACE, "Unsubscribe"); 66 try 67 { 68 this->bus.call_noreply(method); 69 } 70 catch (const SdBusError& e) 71 { 72 log<level::ERR>("Error in unsubscribing from systemd signals", 73 entry("ERROR=%s", e.what())); 74 } 75 76 return; 77 } 78 79 auto Activation::activation(Activations value) -> Activations 80 { 81 82 if ((value != softwareServer::Activation::Activations::Active) && 83 (value != softwareServer::Activation::Activations::Activating)) 84 { 85 redundancyPriority.reset(nullptr); 86 } 87 88 if (value == softwareServer::Activation::Activations::Activating) 89 { 90 #ifdef UBIFS_LAYOUT 91 if (rwVolumeCreated == false && roVolumeCreated == false) 92 { 93 // Enable systemd signals 94 Activation::subscribeToSystemdSignals(); 95 96 parent.freeSpace(*this); 97 98 if (!activationProgress) 99 { 100 activationProgress = 101 std::make_unique<ActivationProgress>(bus, path); 102 } 103 104 if (!activationBlocksTransition) 105 { 106 activationBlocksTransition = 107 std::make_unique<ActivationBlocksTransition>(bus, path); 108 } 109 110 #ifdef WANT_SIGNATURE_VERIFY 111 fs::path uploadDir(IMG_UPLOAD_DIR); 112 if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) 113 { 114 onVerifyFailed(); 115 // Stop the activation process, if fieldMode is enabled. 116 if (parent.control::FieldMode::fieldModeEnabled()) 117 { 118 // Cleanup 119 activationBlocksTransition.reset(nullptr); 120 activationProgress.reset(nullptr); 121 return softwareServer::Activation::activation( 122 softwareServer::Activation::Activations::Failed); 123 } 124 } 125 #endif 126 127 flashWrite(); 128 129 activationProgress->progress(10); 130 } 131 else if (rwVolumeCreated == true && roVolumeCreated == true) 132 { 133 if (ubootEnvVarsUpdated == false) 134 { 135 activationProgress->progress(90); 136 137 if (!redundancyPriority) 138 { 139 redundancyPriority = std::make_unique<RedundancyPriority>( 140 bus, path, *this, 0); 141 } 142 } 143 else 144 { 145 activationProgress->progress(100); 146 147 activationBlocksTransition.reset(nullptr); 148 activationProgress.reset(nullptr); 149 150 rwVolumeCreated = false; 151 roVolumeCreated = false; 152 ubootEnvVarsUpdated = false; 153 Activation::unsubscribeFromSystemdSignals(); 154 155 // Remove version object from image manager 156 Activation::deleteImageManagerObject(); 157 158 // Create active association 159 parent.createActiveAssociation(path); 160 161 if (Activation::checkApplyTimeImmediate() == true) 162 { 163 log<level::INFO>("Image Active. ApplyTime is immediate, " 164 "rebooting BMC."); 165 Activation::rebootBmc(); 166 } 167 168 return softwareServer::Activation::activation( 169 softwareServer::Activation::Activations::Active); 170 } 171 } 172 #else // !UBIFS_LAYOUT 173 174 #ifdef WANT_SIGNATURE_VERIFY 175 fs::path uploadDir(IMG_UPLOAD_DIR); 176 if (!verifySignature(uploadDir / versionId, SIGNED_IMAGE_CONF_PATH)) 177 { 178 onVerifyFailed(); 179 // Stop the activation process, if fieldMode is enabled. 180 if (parent.control::FieldMode::fieldModeEnabled()) 181 { 182 return softwareServer::Activation::activation( 183 softwareServer::Activation::Activations::Failed); 184 } 185 } 186 #endif 187 parent.freeSpace(*this); 188 189 flashWrite(); 190 191 if (!redundancyPriority) 192 { 193 redundancyPriority = 194 std::make_unique<RedundancyPriority>(bus, path, *this, 0); 195 } 196 197 // Remove version object from image manager 198 Activation::deleteImageManagerObject(); 199 200 // Create active association 201 parent.createActiveAssociation(path); 202 203 log<level::INFO>("BMC image ready, need reboot to get activated."); 204 return softwareServer::Activation::activation( 205 softwareServer::Activation::Activations::Active); 206 #endif 207 } 208 else 209 { 210 activationBlocksTransition.reset(nullptr); 211 activationProgress.reset(nullptr); 212 } 213 return softwareServer::Activation::activation(value); 214 } 215 216 void Activation::deleteImageManagerObject() 217 { 218 // Call the Delete object for <versionID> inside image_manager 219 auto method = this->bus.new_method_call(VERSION_BUSNAME, path.c_str(), 220 "xyz.openbmc_project.Object.Delete", 221 "Delete"); 222 try 223 { 224 bus.call_noreply(method); 225 } 226 catch (const SdBusError& e) 227 { 228 log<level::ERR>("Error in Deleting image from image manager", 229 entry("VERSIONPATH=%s", path.c_str())); 230 return; 231 } 232 } 233 234 auto Activation::requestedActivation(RequestedActivations value) 235 -> RequestedActivations 236 { 237 rwVolumeCreated = false; 238 roVolumeCreated = false; 239 ubootEnvVarsUpdated = false; 240 241 if ((value == softwareServer::Activation::RequestedActivations::Active) && 242 (softwareServer::Activation::requestedActivation() != 243 softwareServer::Activation::RequestedActivations::Active)) 244 { 245 if ((softwareServer::Activation::activation() == 246 softwareServer::Activation::Activations::Ready) || 247 (softwareServer::Activation::activation() == 248 softwareServer::Activation::Activations::Failed)) 249 { 250 Activation::activation( 251 softwareServer::Activation::Activations::Activating); 252 } 253 } 254 return softwareServer::Activation::requestedActivation(value); 255 } 256 257 uint8_t RedundancyPriority::priority(uint8_t value) 258 { 259 // Set the priority value so that the freePriority() function can order 260 // the versions by priority. 261 auto newPriority = softwareServer::RedundancyPriority::priority(value); 262 parent.parent.savePriority(parent.versionId, value); 263 parent.parent.freePriority(value, parent.versionId); 264 return newPriority; 265 } 266 267 uint8_t RedundancyPriority::sdbusPriority(uint8_t value) 268 { 269 parent.parent.savePriority(parent.versionId, value); 270 return softwareServer::RedundancyPriority::priority(value); 271 } 272 273 void Activation::unitStateChange(sdbusplus::message::message& msg) 274 { 275 if (softwareServer::Activation::activation() != 276 softwareServer::Activation::Activations::Activating) 277 { 278 return; 279 } 280 281 onStateChanges(msg); 282 283 return; 284 } 285 286 #ifdef WANT_SIGNATURE_VERIFY 287 bool Activation::verifySignature(const fs::path& imageDir, 288 const fs::path& confDir) 289 { 290 using Signature = phosphor::software::image::Signature; 291 292 Signature signature(imageDir, confDir); 293 294 return signature.verify(); 295 } 296 297 void Activation::onVerifyFailed() 298 { 299 log<level::ERR>("Error occurred during image validation"); 300 report<InternalFailure>(); 301 } 302 #endif 303 304 void ActivationBlocksTransition::enableRebootGuard() 305 { 306 log<level::INFO>("BMC image activating - BMC reboots are disabled."); 307 308 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 309 SYSTEMD_INTERFACE, "StartUnit"); 310 method.append("reboot-guard-enable.service", "replace"); 311 bus.call_noreply(method); 312 } 313 314 void ActivationBlocksTransition::disableRebootGuard() 315 { 316 log<level::INFO>("BMC activation has ended - BMC reboots are re-enabled."); 317 318 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 319 SYSTEMD_INTERFACE, "StartUnit"); 320 method.append("reboot-guard-disable.service", "replace"); 321 bus.call_noreply(method); 322 } 323 324 bool Activation::checkApplyTimeImmediate() 325 { 326 auto service = utils::getService(bus, applyTimeObjPath, applyTimeIntf); 327 if (service.empty()) 328 { 329 log<level::INFO>("Error getting the service name for BMC image " 330 "ApplyTime. The BMC needs to be manually rebooted to " 331 "complete the image activation if needed " 332 "immediately."); 333 } 334 else 335 { 336 337 auto method = bus.new_method_call(service.c_str(), applyTimeObjPath, 338 dbusPropIntf, "Get"); 339 method.append(applyTimeIntf, applyTimeProp); 340 341 try 342 { 343 auto reply = bus.call(method); 344 345 sdbusplus::message::variant<std::string> result; 346 reply.read(result); 347 auto applyTime = 348 sdbusplus::message::variant_ns::get<std::string>(result); 349 if (applyTime == applyTimeImmediate) 350 { 351 return true; 352 } 353 } 354 catch (const SdBusError& e) 355 { 356 log<level::ERR>("Error in getting ApplyTime", 357 entry("ERROR=%s", e.what())); 358 } 359 } 360 return false; 361 } 362 363 void Activation::rebootBmc() 364 { 365 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 366 SYSTEMD_INTERFACE, "StartUnit"); 367 method.append("force-reboot.service", "replace"); 368 try 369 { 370 auto reply = bus.call(method); 371 } 372 catch (const SdBusError& e) 373 { 374 log<level::ALERT>("Error in trying to reboot the BMC. " 375 "The BMC needs to be manually rebooted to complete " 376 "the image activation."); 377 report<InternalFailure>(); 378 } 379 } 380 381 } // namespace updater 382 } // namespace software 383 } // namespace phosphor 384