xref: /openbmc/pldm/fw-update/update_manager.cpp (revision ca1998f3)
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