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