1 #include "update_manager.hpp" 2 3 #include "activation.hpp" 4 #include "common/utils.hpp" 5 #include "package_parser.hpp" 6 7 #include <phosphor-logging/lg2.hpp> 8 9 #include <cassert> 10 #include <cmath> 11 #include <filesystem> 12 #include <fstream> 13 #include <string> 14 15 PHOSPHOR_LOG2_USING; 16 17 namespace pldm 18 { 19 20 namespace fw_update 21 { 22 23 namespace fs = std::filesystem; 24 namespace software = sdbusplus::xyz::openbmc_project::Software::server; 25 26 int UpdateManager::processPackage(const std::filesystem::path& packageFilePath) 27 { 28 // If no devices discovered, take no action on the package. 29 if (!descriptorMap.size()) 30 { 31 return 0; 32 } 33 34 namespace software = sdbusplus::xyz::openbmc_project::Software::server; 35 // If a firmware activation of a package is in progress, don't proceed with 36 // package processing 37 if (activation) 38 { 39 if (activation->activation() == 40 software::Activation::Activations::Activating) 41 { 42 error( 43 "Activation of PLDM fw update package for version '{VERSION}' already in progress.", 44 "VERSION", parser->pkgVersion); 45 std::filesystem::remove(packageFilePath); 46 return -1; 47 } 48 else 49 { 50 clearActivationInfo(); 51 } 52 } 53 54 package.open(packageFilePath, 55 std::ios::binary | std::ios::in | std::ios::ate); 56 if (!package.good()) 57 { 58 error( 59 "Failed to open the PLDM fw update package file '{FILE}', error - {ERROR}.", 60 "ERROR", unsigned(errno), "FILE", packageFilePath.c_str()); 61 package.close(); 62 std::filesystem::remove(packageFilePath); 63 return -1; 64 } 65 66 uintmax_t packageSize = package.tellg(); 67 if (packageSize < sizeof(pldm_package_header_information)) 68 { 69 error( 70 "PLDM fw update package length {SIZE} less than the length of the package header information '{PACKAGE_HEADER_INFO_SIZE}'.", 71 "SIZE", packageSize, "PACKAGE_HEADER_INFO_SIZE", 72 sizeof(pldm_package_header_information)); 73 package.close(); 74 std::filesystem::remove(packageFilePath); 75 return -1; 76 } 77 78 package.seekg(0); 79 std::vector<uint8_t> packageHeader(sizeof(pldm_package_header_information)); 80 package.read(reinterpret_cast<char*>(packageHeader.data()), 81 sizeof(pldm_package_header_information)); 82 83 auto pkgHeaderInfo = 84 reinterpret_cast<const pldm_package_header_information*>( 85 packageHeader.data()); 86 auto pkgHeaderInfoSize = sizeof(pldm_package_header_information) + 87 pkgHeaderInfo->package_version_string_length; 88 packageHeader.clear(); 89 packageHeader.resize(pkgHeaderInfoSize); 90 package.seekg(0); 91 package.read(reinterpret_cast<char*>(packageHeader.data()), 92 pkgHeaderInfoSize); 93 94 parser = parsePkgHeader(packageHeader); 95 if (parser == nullptr) 96 { 97 error("Invalid PLDM package header information"); 98 package.close(); 99 std::filesystem::remove(packageFilePath); 100 return -1; 101 } 102 103 // Populate object path with the hash of the package version 104 size_t versionHash = std::hash<std::string>{}(parser->pkgVersion); 105 objPath = swRootPath + std::to_string(versionHash); 106 107 package.seekg(0); 108 packageHeader.resize(parser->pkgHeaderSize); 109 package.read(reinterpret_cast<char*>(packageHeader.data()), 110 parser->pkgHeaderSize); 111 try 112 { 113 parser->parse(packageHeader, packageSize); 114 } 115 catch (const std::exception& e) 116 { 117 error("Invalid PLDM package header, error - {ERROR}", "ERROR", e); 118 activation = std::make_unique<Activation>( 119 pldm::utils::DBusHandler::getBus(), objPath, 120 software::Activation::Activations::Invalid, this); 121 package.close(); 122 parser.reset(); 123 return -1; 124 } 125 126 auto deviceUpdaterInfos = 127 associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap, 128 totalNumComponentUpdates); 129 if (!deviceUpdaterInfos.size()) 130 { 131 error( 132 "No matching devices found with the PLDM firmware update package"); 133 activation = std::make_unique<Activation>( 134 pldm::utils::DBusHandler::getBus(), objPath, 135 software::Activation::Activations::Invalid, this); 136 package.close(); 137 parser.reset(); 138 return 0; 139 } 140 141 const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords(); 142 const auto& compImageInfos = parser->getComponentImageInfos(); 143 144 for (const auto& deviceUpdaterInfo : deviceUpdaterInfos) 145 { 146 const auto& fwDeviceIDRecord = 147 fwDeviceIDRecords[deviceUpdaterInfo.second]; 148 auto search = componentInfoMap.find(deviceUpdaterInfo.first); 149 deviceUpdaterMap.emplace( 150 deviceUpdaterInfo.first, 151 std::make_unique<DeviceUpdater>( 152 deviceUpdaterInfo.first, package, fwDeviceIDRecord, 153 compImageInfos, search->second, MAXIMUM_TRANSFER_SIZE, this)); 154 } 155 156 fwPackageFilePath = packageFilePath; 157 activation = std::make_unique<Activation>( 158 pldm::utils::DBusHandler::getBus(), objPath, 159 software::Activation::Activations::Ready, this); 160 activationProgress = std::make_unique<ActivationProgress>( 161 pldm::utils::DBusHandler::getBus(), objPath); 162 163 return 0; 164 } 165 166 DeviceUpdaterInfos UpdateManager::associatePkgToDevices( 167 const FirmwareDeviceIDRecords& fwDeviceIDRecords, 168 const DescriptorMap& descriptorMap, 169 TotalComponentUpdates& totalNumComponentUpdates) 170 { 171 DeviceUpdaterInfos deviceUpdaterInfos; 172 for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index) 173 { 174 const auto& deviceIDDescriptors = 175 std::get<Descriptors>(fwDeviceIDRecords[index]); 176 for (const auto& [eid, descriptors] : descriptorMap) 177 { 178 if (std::includes(descriptors.begin(), descriptors.end(), 179 deviceIDDescriptors.begin(), 180 deviceIDDescriptors.end())) 181 { 182 deviceUpdaterInfos.emplace_back(std::make_pair(eid, index)); 183 const auto& applicableComponents = 184 std::get<ApplicableComponents>(fwDeviceIDRecords[index]); 185 totalNumComponentUpdates += applicableComponents.size(); 186 } 187 } 188 } 189 return deviceUpdaterInfos; 190 } 191 192 void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status) 193 { 194 deviceUpdateCompletionMap.emplace(eid, status); 195 if (deviceUpdateCompletionMap.size() == deviceUpdaterMap.size()) 196 { 197 for (const auto& [eid, status] : deviceUpdateCompletionMap) 198 { 199 if (!status) 200 { 201 activation->activation( 202 software::Activation::Activations::Failed); 203 return; 204 } 205 } 206 207 auto endTime = std::chrono::steady_clock::now(); 208 auto dur = 209 std::chrono::duration<double, std::milli>(endTime - startTime) 210 .count(); 211 info("Firmware update time: {DURATION}ms", "DURATION", dur); 212 activation->activation(software::Activation::Activations::Active); 213 } 214 return; 215 } 216 217 Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command, 218 const pldm_msg* request, size_t reqMsgLen) 219 { 220 Response response(sizeof(pldm_msg), 0); 221 if (deviceUpdaterMap.contains(eid)) 222 { 223 auto search = deviceUpdaterMap.find(eid); 224 if (command == PLDM_REQUEST_FIRMWARE_DATA) 225 { 226 return search->second->requestFwData(request, reqMsgLen); 227 } 228 else if (command == PLDM_TRANSFER_COMPLETE) 229 { 230 return search->second->transferComplete(request, reqMsgLen); 231 } 232 else if (command == PLDM_VERIFY_COMPLETE) 233 { 234 return search->second->verifyComplete(request, reqMsgLen); 235 } 236 else if (command == PLDM_APPLY_COMPLETE) 237 { 238 return search->second->applyComplete(request, reqMsgLen); 239 } 240 else 241 { 242 auto ptr = reinterpret_cast<pldm_msg*>(response.data()); 243 auto rc = encode_cc_only_resp( 244 request->hdr.instance_id, request->hdr.type, 245 request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr); 246 assert(rc == PLDM_SUCCESS); 247 } 248 } 249 else 250 { 251 auto ptr = reinterpret_cast<pldm_msg*>(response.data()); 252 auto rc = encode_cc_only_resp(request->hdr.instance_id, 253 request->hdr.type, +request->hdr.command, 254 PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr); 255 assert(rc == PLDM_SUCCESS); 256 } 257 258 return response; 259 } 260 261 void UpdateManager::activatePackage() 262 { 263 startTime = std::chrono::steady_clock::now(); 264 for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap) 265 { 266 deviceUpdaterPtr->startFwUpdateFlow(); 267 } 268 } 269 270 void UpdateManager::clearActivationInfo() 271 { 272 activation.reset(); 273 activationProgress.reset(); 274 objPath.clear(); 275 276 deviceUpdaterMap.clear(); 277 deviceUpdateCompletionMap.clear(); 278 parser.reset(); 279 package.close(); 280 std::filesystem::remove(fwPackageFilePath); 281 totalNumComponentUpdates = 0; 282 compUpdateCompletedCount = 0; 283 } 284 285 void UpdateManager::updateActivationProgress() 286 { 287 compUpdateCompletedCount++; 288 auto progressPercent = static_cast<uint8_t>(std::floor( 289 (100 * compUpdateCompletedCount) / totalNumComponentUpdates)); 290 activationProgress->progress(progressPercent); 291 } 292 293 } // namespace fw_update 294 295 } // namespace pldm 296