xref: /openbmc/pldm/fw-update/update_manager.cpp (revision 4d8d57701e18e7eb209c6dad5d13b847c5e4d92e)
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(deviceUpdaterInfo.first,
149                                  std::make_unique<DeviceUpdater>(
150                                      deviceUpdaterInfo.first, event, requester,
151                                      handler, package, fwDeviceIDRecord,
152                                      compImageInfos, search->second,
153                                      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         std::cerr << "Firmware update time: "
209                   << std::chrono::duration<double, std::milli>(endTime -
210                                                                startTime)
211                          .count()
212                   << " ms\n";
213         activation->activation(software::Activation::Activations::Active);
214     }
215     return;
216 }
217 
218 Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command,
219                                       const pldm_msg* request, size_t reqMsgLen)
220 {
221     Response response(sizeof(pldm_msg), 0);
222     if (deviceUpdaterMap.contains(eid))
223     {
224         auto search = deviceUpdaterMap.find(eid);
225         if (command == PLDM_REQUEST_FIRMWARE_DATA)
226         {
227             return search->second->requestFwData(request, reqMsgLen);
228         }
229         else if (command == PLDM_TRANSFER_COMPLETE)
230         {
231             return search->second->transferComplete(request, reqMsgLen);
232         }
233         else if (command == PLDM_VERIFY_COMPLETE)
234         {
235             return search->second->verifyComplete(request, reqMsgLen);
236         }
237         else if (command == PLDM_APPLY_COMPLETE)
238         {
239             return search->second->applyComplete(request, reqMsgLen);
240         }
241         else
242         {
243             auto ptr = reinterpret_cast<pldm_msg*>(response.data());
244             auto rc = encode_cc_only_resp(
245                 request->hdr.instance_id, request->hdr.type,
246                 request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr);
247             assert(rc == PLDM_SUCCESS);
248         }
249     }
250     else
251     {
252         auto ptr = reinterpret_cast<pldm_msg*>(response.data());
253         auto rc = encode_cc_only_resp(request->hdr.instance_id,
254                                       request->hdr.type, +request->hdr.command,
255                                       PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr);
256         assert(rc == PLDM_SUCCESS);
257     }
258 
259     return response;
260 }
261 
262 void UpdateManager::activatePackage()
263 {
264     startTime = std::chrono::steady_clock::now();
265     for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap)
266     {
267         deviceUpdaterPtr->startFwUpdateFlow();
268     }
269 }
270 
271 void UpdateManager::clearActivationInfo()
272 {
273     activation.reset();
274     activationProgress.reset();
275     objPath.clear();
276 
277     deviceUpdaterMap.clear();
278     deviceUpdateCompletionMap.clear();
279     parser.reset();
280     package.close();
281     std::filesystem::remove(fwPackageFilePath);
282     totalNumComponentUpdates = 0;
283     compUpdateCompletedCount = 0;
284 }
285 
286 void UpdateManager::updateActivationProgress()
287 {
288     compUpdateCompletedCount++;
289     auto progressPercent = static_cast<uint8_t>(std::floor(
290         (100 * compUpdateCompletedCount) / totalNumComponentUpdates));
291     activationProgress->progress(progressPercent);
292 }
293 
294 } // namespace fw_update
295 
296 } // namespace pldm