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