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 36 if (activation->activation() == 37 software::Activation::Activations::Activating) 38 { 39 std::cerr 40 << "Activation of PLDM FW update package already in progress" 41 << ", PACKAGE_VERSION=" << parser->pkgVersion << "\n"; 42 std::filesystem::remove(packageFilePath); 43 return -1; 44 } 45 else 46 { 47 clearActivationInfo(); 48 } 49 } 50 51 package.open(packageFilePath, 52 std::ios::binary | std::ios::in | std::ios::ate); 53 if (!package.good()) 54 { 55 std::cerr << "Opening the PLDM FW update package failed, ERR=" 56 << unsigned(errno) << ", PACKAGEFILE=" << packageFilePath 57 << "\n"; 58 package.close(); 59 std::filesystem::remove(packageFilePath); 60 return -1; 61 } 62 63 uintmax_t packageSize = package.tellg(); 64 if (packageSize < sizeof(pldm_package_header_information)) 65 { 66 std::cerr << "PLDM FW update package length less than the length of " 67 "the package header information, PACKAGESIZE=" 68 << packageSize << "\n"; 69 package.close(); 70 std::filesystem::remove(packageFilePath); 71 return -1; 72 } 73 74 package.seekg(0); 75 std::vector<uint8_t> packageHeader(sizeof(pldm_package_header_information)); 76 package.read(reinterpret_cast<char*>(packageHeader.data()), 77 sizeof(pldm_package_header_information)); 78 79 auto pkgHeaderInfo = 80 reinterpret_cast<const pldm_package_header_information*>( 81 packageHeader.data()); 82 auto pkgHeaderInfoSize = sizeof(pldm_package_header_information) + 83 pkgHeaderInfo->package_version_string_length; 84 packageHeader.clear(); 85 packageHeader.resize(pkgHeaderInfoSize); 86 package.seekg(0); 87 package.read(reinterpret_cast<char*>(packageHeader.data()), 88 pkgHeaderInfoSize); 89 90 parser = parsePkgHeader(packageHeader); 91 if (parser == nullptr) 92 { 93 std::cerr << "Invalid PLDM package header information" 94 << "\n"; 95 package.close(); 96 std::filesystem::remove(packageFilePath); 97 return -1; 98 } 99 100 // Populate object path with the hash of the package version 101 size_t versionHash = std::hash<std::string>{}(parser->pkgVersion); 102 objPath = swRootPath + std::to_string(versionHash); 103 104 package.seekg(0); 105 packageHeader.resize(parser->pkgHeaderSize); 106 package.read(reinterpret_cast<char*>(packageHeader.data()), 107 parser->pkgHeaderSize); 108 try 109 { 110 parser->parse(packageHeader, packageSize); 111 } 112 catch (const std::exception& e) 113 { 114 std::cerr << "Invalid PLDM package header" 115 << "\n"; 116 activation = std::make_unique<Activation>( 117 pldm::utils::DBusHandler::getBus(), objPath, 118 software::Activation::Activations::Invalid, this); 119 package.close(); 120 parser.reset(); 121 return -1; 122 } 123 124 auto deviceUpdaterInfos = 125 associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap, 126 totalNumComponentUpdates); 127 if (!deviceUpdaterInfos.size()) 128 { 129 std::cerr 130 << "No matching devices found with the PLDM firmware update package" 131 << "\n"; 132 activation = std::make_unique<Activation>( 133 pldm::utils::DBusHandler::getBus(), objPath, 134 software::Activation::Activations::Invalid, this); 135 package.close(); 136 parser.reset(); 137 return 0; 138 } 139 140 const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords(); 141 const auto& compImageInfos = parser->getComponentImageInfos(); 142 143 for (const auto& deviceUpdaterInfo : deviceUpdaterInfos) 144 { 145 const auto& fwDeviceIDRecord = 146 fwDeviceIDRecords[deviceUpdaterInfo.second]; 147 auto search = componentInfoMap.find(deviceUpdaterInfo.first); 148 deviceUpdaterMap.emplace( 149 deviceUpdaterInfo.first, 150 std::make_unique<DeviceUpdater>( 151 deviceUpdaterInfo.first, package, fwDeviceIDRecord, 152 compImageInfos, search->second, MAXIMUM_TRANSFER_SIZE, this)); 153 } 154 155 fwPackageFilePath = packageFilePath; 156 activation = std::make_unique<Activation>( 157 pldm::utils::DBusHandler::getBus(), objPath, 158 software::Activation::Activations::Ready, this); 159 activationProgress = std::make_unique<ActivationProgress>( 160 pldm::utils::DBusHandler::getBus(), objPath); 161 162 return 0; 163 } 164 165 DeviceUpdaterInfos UpdateManager::associatePkgToDevices( 166 const FirmwareDeviceIDRecords& fwDeviceIDRecords, 167 const DescriptorMap& descriptorMap, 168 TotalComponentUpdates& totalNumComponentUpdates) 169 { 170 DeviceUpdaterInfos deviceUpdaterInfos; 171 for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index) 172 { 173 const auto& deviceIDDescriptors = 174 std::get<Descriptors>(fwDeviceIDRecords[index]); 175 for (const auto& [eid, descriptors] : descriptorMap) 176 { 177 if (std::includes(descriptors.begin(), descriptors.end(), 178 deviceIDDescriptors.begin(), 179 deviceIDDescriptors.end())) 180 { 181 deviceUpdaterInfos.emplace_back(std::make_pair(eid, index)); 182 const auto& applicableComponents = 183 std::get<ApplicableComponents>(fwDeviceIDRecords[index]); 184 totalNumComponentUpdates += applicableComponents.size(); 185 } 186 } 187 } 188 return deviceUpdaterInfos; 189 } 190 191 void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status) 192 { 193 deviceUpdateCompletionMap.emplace(eid, status); 194 if (deviceUpdateCompletionMap.size() == deviceUpdaterMap.size()) 195 { 196 for (const auto& [eid, status] : deviceUpdateCompletionMap) 197 { 198 if (!status) 199 { 200 activation->activation( 201 software::Activation::Activations::Failed); 202 return; 203 } 204 } 205 206 auto endTime = std::chrono::steady_clock::now(); 207 std::cerr << "Firmware update time: " 208 << std::chrono::duration<double, std::milli>(endTime - 209 startTime) 210 .count() 211 << " ms\n"; 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