1 #include "config.h" 2 3 #include "activation.hpp" 4 5 #include "item_updater.hpp" 6 #include "serialize.hpp" 7 8 #include <experimental/filesystem> 9 #include <phosphor-logging/log.hpp> 10 #include <sdbusplus/exception.hpp> 11 12 #ifdef WANT_SIGNATURE_VERIFY 13 #include "image_verify.hpp" 14 15 #include <phosphor-logging/elog-errors.hpp> 16 #include <phosphor-logging/elog.hpp> 17 #include <sdbusplus/server.hpp> 18 #include <xyz/openbmc_project/Common/error.hpp> 19 #endif 20 21 namespace openpower 22 { 23 namespace software 24 { 25 namespace updater 26 { 27 28 namespace fs = std::experimental::filesystem; 29 namespace softwareServer = sdbusplus::xyz::openbmc_project::Software::server; 30 31 using namespace phosphor::logging; 32 using sdbusplus::exception::SdBusError; 33 34 #ifdef WANT_SIGNATURE_VERIFY 35 using InternalFailure = 36 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 37 38 // Field mode path and interface. 39 constexpr auto FIELDMODE_PATH("/xyz/openbmc_project/software"); 40 constexpr auto FIELDMODE_INTERFACE("xyz.openbmc_project.Control.FieldMode"); 41 #endif 42 43 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1"; 44 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1"; 45 46 void Activation::subscribeToSystemdSignals() 47 { 48 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 49 SYSTEMD_INTERFACE, "Subscribe"); 50 try 51 { 52 this->bus.call_noreply(method); 53 } 54 catch (const SdBusError& e) 55 { 56 if (e.name() != nullptr && 57 strcmp("org.freedesktop.systemd1.AlreadySubscribed", e.name()) == 0) 58 { 59 // If an Activation attempt fails, the Unsubscribe method is not 60 // called. This may lead to an AlreadySubscribed error if the 61 // Activation is re-attempted. 62 } 63 else 64 { 65 log<level::ERR>("Error subscribing to systemd", 66 entry("ERROR=%s", e.what())); 67 } 68 } 69 return; 70 } 71 72 void Activation::unsubscribeFromSystemdSignals() 73 { 74 auto method = this->bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH, 75 SYSTEMD_INTERFACE, "Unsubscribe"); 76 this->bus.call_noreply(method); 77 78 return; 79 } 80 81 void Activation::startActivation() 82 { 83 // Since the squashfs image has not yet been loaded to pnor and the 84 // RW volumes have not yet been created, we need to start the 85 // service files for each of those actions. 86 87 if (!activationProgress) 88 { 89 activationProgress = std::make_unique<ActivationProgress>(bus, path); 90 } 91 92 if (!activationBlocksTransition) 93 { 94 activationBlocksTransition = 95 std::make_unique<ActivationBlocksTransition>(bus, path); 96 } 97 98 constexpr auto ubimountService = "obmc-flash-bios-ubimount@"; 99 auto ubimountServiceFile = 100 std::string(ubimountService) + versionId + ".service"; 101 auto method = bus.new_method_call(SYSTEMD_BUSNAME, SYSTEMD_PATH, 102 SYSTEMD_INTERFACE, "StartUnit"); 103 method.append(ubimountServiceFile, "replace"); 104 bus.call_noreply(method); 105 106 activationProgress->progress(10); 107 } 108 109 void Activation::finishActivation() 110 { 111 activationProgress->progress(90); 112 113 // Set Redundancy Priority before setting to Active 114 if (!redundancyPriority) 115 { 116 redundancyPriority = 117 std::make_unique<RedundancyPriority>(bus, path, *this, 0); 118 } 119 120 activationProgress->progress(100); 121 122 activationBlocksTransition.reset(nullptr); 123 activationProgress.reset(nullptr); 124 125 ubiVolumesCreated = false; 126 Activation::unsubscribeFromSystemdSignals(); 127 // Remove version object from image manager 128 Activation::deleteImageManagerObject(); 129 // Create active association 130 parent.createActiveAssociation(path); 131 } 132 133 auto Activation::activation(Activations value) -> Activations 134 { 135 136 if (value != softwareServer::Activation::Activations::Active) 137 { 138 redundancyPriority.reset(nullptr); 139 } 140 141 if (value == softwareServer::Activation::Activations::Activating) 142 { 143 parent.freeSpace(); 144 softwareServer::Activation::activation(value); 145 146 if (ubiVolumesCreated == false) 147 { 148 // Enable systemd signals 149 Activation::subscribeToSystemdSignals(); 150 151 #ifdef WANT_SIGNATURE_VERIFY 152 // Validate the signed image. 153 if (!validateSignature()) 154 { 155 // Cleanup 156 activationBlocksTransition.reset(nullptr); 157 activationProgress.reset(nullptr); 158 159 return softwareServer::Activation::activation( 160 softwareServer::Activation::Activations::Failed); 161 } 162 #endif 163 Activation::startActivation(); 164 return softwareServer::Activation::activation(value); 165 } 166 else if (ubiVolumesCreated == true) 167 { 168 // Only when the squashfs image is finished loading AND the RW 169 // volumes have been created do we proceed with activation. To 170 // verify that this happened, we check for the mount dirs PNOR_PRSV 171 // and PNOR_RW_PREFIX_<versionid>, as well as the image dir R0. 172 173 if ((fs::is_directory(PNOR_PRSV)) && 174 (fs::is_directory(PNOR_RW_PREFIX + versionId)) && 175 (fs::is_directory(PNOR_RO_PREFIX + versionId))) 176 { 177 Activation::finishActivation(); 178 return softwareServer::Activation::activation( 179 softwareServer::Activation::Activations::Active); 180 } 181 else 182 { 183 activationBlocksTransition.reset(nullptr); 184 activationProgress.reset(nullptr); 185 return softwareServer::Activation::activation( 186 softwareServer::Activation::Activations::Failed); 187 } 188 } 189 } 190 else 191 { 192 activationBlocksTransition.reset(nullptr); 193 activationProgress.reset(nullptr); 194 } 195 196 return softwareServer::Activation::activation(value); 197 } 198 199 auto Activation::requestedActivation(RequestedActivations value) 200 -> RequestedActivations 201 { 202 ubiVolumesCreated = false; 203 204 if ((value == softwareServer::Activation::RequestedActivations::Active) && 205 (softwareServer::Activation::requestedActivation() != 206 softwareServer::Activation::RequestedActivations::Active)) 207 { 208 if ((softwareServer::Activation::activation() == 209 softwareServer::Activation::Activations::Ready) || 210 (softwareServer::Activation::activation() == 211 softwareServer::Activation::Activations::Failed)) 212 { 213 Activation::activation( 214 softwareServer::Activation::Activations::Activating); 215 } 216 } 217 return softwareServer::Activation::requestedActivation(value); 218 } 219 220 void Activation::deleteImageManagerObject() 221 { 222 // Get the Delete object for <versionID> inside image_manager 223 constexpr auto versionServiceStr = "xyz.openbmc_project.Software.Version"; 224 constexpr auto deleteInterface = "xyz.openbmc_project.Object.Delete"; 225 std::string versionService; 226 auto method = this->bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 227 MAPPER_INTERFACE, "GetObject"); 228 229 method.append(path); 230 method.append(std::vector<std::string>({deleteInterface})); 231 auto mapperResponseMsg = bus.call(method); 232 if (mapperResponseMsg.is_method_error()) 233 { 234 log<level::ERR>("Error in Get Delete Object", 235 entry("VERSIONPATH=%s", path.c_str())); 236 return; 237 } 238 std::map<std::string, std::vector<std::string>> mapperResponse; 239 mapperResponseMsg.read(mapperResponse); 240 if (mapperResponse.begin() == mapperResponse.end()) 241 { 242 log<level::ERR>("ERROR in reading the mapper response", 243 entry("VERSIONPATH=%s", path.c_str())); 244 return; 245 } 246 247 // We need to find the phosphor-software-manager's version service 248 // to invoke the delete interface 249 for (auto resp : mapperResponse) 250 { 251 if (resp.first.find(versionServiceStr) != std::string::npos) 252 { 253 versionService = resp.first; 254 } 255 } 256 257 if (versionService.empty()) 258 { 259 log<level::ERR>("Error finding version service"); 260 return; 261 } 262 263 // Call the Delete object for <versionID> inside image_manager 264 method = this->bus.new_method_call(versionService.c_str(), path.c_str(), 265 deleteInterface, "Delete"); 266 try 267 { 268 auto mapperResponseMsg = bus.call(method); 269 270 // Check that the bus call didn't result in an error 271 if (mapperResponseMsg.is_method_error()) 272 { 273 log<level::ERR>("Error in Deleting image from image manager", 274 entry("VERSIONPATH=%s", path.c_str())); 275 return; 276 } 277 } 278 catch (const SdBusError& e) 279 { 280 if (e.name() != nullptr && strcmp("System.Error.ELOOP", e.name()) == 0) 281 { 282 // TODO: Error being tracked with openbmc/openbmc#3311 283 } 284 else 285 { 286 log<level::ERR>("Error performing call to Delete object path", 287 entry("ERROR=%s", e.what()), 288 entry("PATH=%s", path.c_str())); 289 } 290 return; 291 } 292 } 293 294 uint8_t RedundancyPriority::priority(uint8_t value) 295 { 296 parent.parent.freePriority(value, parent.versionId); 297 storeToFile(parent.versionId, value); 298 return softwareServer::RedundancyPriority::priority(value); 299 } 300 301 void Activation::unitStateChange(sdbusplus::message::message& msg) 302 { 303 uint32_t newStateID{}; 304 sdbusplus::message::object_path newStateObjPath; 305 std::string newStateUnit{}; 306 std::string newStateResult{}; 307 308 // Read the msg and populate each variable 309 msg.read(newStateID, newStateObjPath, newStateUnit, newStateResult); 310 311 auto ubimountServiceFile = 312 "obmc-flash-bios-ubimount@" + versionId + ".service"; 313 314 if (newStateUnit == ubimountServiceFile && newStateResult == "done") 315 { 316 ubiVolumesCreated = true; 317 activationProgress->progress(activationProgress->progress() + 50); 318 } 319 320 if (ubiVolumesCreated) 321 { 322 Activation::activation( 323 softwareServer::Activation::Activations::Activating); 324 } 325 326 if ((newStateUnit == ubimountServiceFile) && 327 (newStateResult == "failed" || newStateResult == "dependency")) 328 { 329 Activation::activation(softwareServer::Activation::Activations::Failed); 330 } 331 332 return; 333 } 334 335 #ifdef WANT_SIGNATURE_VERIFY 336 inline bool Activation::validateSignature() 337 { 338 using Signature = openpower::software::image::Signature; 339 fs::path imageDir(IMG_DIR); 340 341 Signature signature(imageDir / versionId, PNOR_SIGNED_IMAGE_CONF_PATH); 342 343 // Validate the signed image. 344 if (signature.verify()) 345 { 346 return true; 347 } 348 // Log error and continue activation process, if field mode disabled. 349 log<level::ERR>("Error occurred during image validation"); 350 report<InternalFailure>(); 351 352 try 353 { 354 if (!fieldModeEnabled()) 355 { 356 return true; 357 } 358 } 359 catch (const InternalFailure& e) 360 { 361 report<InternalFailure>(); 362 } 363 return false; 364 } 365 366 bool Activation::fieldModeEnabled() 367 { 368 auto fieldModeSvc = getService(bus, FIELDMODE_PATH, FIELDMODE_INTERFACE); 369 370 auto method = bus.new_method_call(fieldModeSvc.c_str(), FIELDMODE_PATH, 371 "org.freedesktop.DBus.Properties", "Get"); 372 373 method.append(FIELDMODE_INTERFACE, "FieldModeEnabled"); 374 auto reply = bus.call(method); 375 if (reply.is_method_error()) 376 { 377 log<level::ERR>("Error in fieldModeEnabled getValue"); 378 elog<InternalFailure>(); 379 } 380 sdbusplus::message::variant<bool> fieldMode; 381 reply.read(fieldMode); 382 383 return sdbusplus::message::variant_ns::get<bool>(fieldMode); 384 } 385 386 std::string Activation::getService(sdbusplus::bus::bus& bus, 387 const std::string& path, 388 const std::string& intf) 389 { 390 auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH, 391 MAPPER_INTERFACE, "GetObject"); 392 393 mapperCall.append(path); 394 mapperCall.append(std::vector<std::string>({intf})); 395 396 auto mapperResponseMsg = bus.call(mapperCall); 397 398 if (mapperResponseMsg.is_method_error()) 399 { 400 log<level::ERR>("ERROR in getting service", 401 entry("PATH=%s", path.c_str()), 402 entry("INTERFACE=%s", intf.c_str())); 403 404 elog<InternalFailure>(); 405 } 406 407 std::map<std::string, std::vector<std::string>> mapperResponse; 408 mapperResponseMsg.read(mapperResponse); 409 410 if (mapperResponse.begin() == mapperResponse.end()) 411 { 412 log<level::ERR>("ERROR reading mapper response", 413 entry("PATH=%s", path.c_str()), 414 entry("INTERFACE=%s", intf.c_str())); 415 416 elog<InternalFailure>(); 417 } 418 return mapperResponse.begin()->first; 419 } 420 #endif 421 422 } // namespace updater 423 } // namespace software 424 } // namespace openpower 425