xref: /openbmc/pldm/fw-update/update_manager.cpp (revision 5ef5b340)
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 already in progress, PACKAGE_VERSION={PKG_VERS}",
44                 "PKG_VERS", 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             "Opening the PLDM FW update package failed, ERR={ERR}, PACKAGEFILE={PKG_FILE}",
60             "ERR", unsigned(errno), "PKG_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 less than the length of the package header information, PACKAGESIZE={PKG_SIZE}",
71             "PKG_SIZE", packageSize);
72         package.close();
73         std::filesystem::remove(packageFilePath);
74         return -1;
75     }
76 
77     package.seekg(0);
78     std::vector<uint8_t> packageHeader(sizeof(pldm_package_header_information));
79     package.read(reinterpret_cast<char*>(packageHeader.data()),
80                  sizeof(pldm_package_header_information));
81 
82     auto pkgHeaderInfo =
83         reinterpret_cast<const pldm_package_header_information*>(
84             packageHeader.data());
85     auto pkgHeaderInfoSize = sizeof(pldm_package_header_information) +
86                              pkgHeaderInfo->package_version_string_length;
87     packageHeader.clear();
88     packageHeader.resize(pkgHeaderInfoSize);
89     package.seekg(0);
90     package.read(reinterpret_cast<char*>(packageHeader.data()),
91                  pkgHeaderInfoSize);
92 
93     parser = parsePkgHeader(packageHeader);
94     if (parser == nullptr)
95     {
96         error("Invalid PLDM package header information");
97         package.close();
98         std::filesystem::remove(packageFilePath);
99         return -1;
100     }
101 
102     // Populate object path with the hash of the package version
103     size_t versionHash = std::hash<std::string>{}(parser->pkgVersion);
104     objPath = swRootPath + std::to_string(versionHash);
105 
106     package.seekg(0);
107     packageHeader.resize(parser->pkgHeaderSize);
108     package.read(reinterpret_cast<char*>(packageHeader.data()),
109                  parser->pkgHeaderSize);
110     try
111     {
112         parser->parse(packageHeader, packageSize);
113     }
114     catch (const std::exception& e)
115     {
116         error("Invalid PLDM package header: {ERROR}", "ERROR", e);
117         activation = std::make_unique<Activation>(
118             pldm::utils::DBusHandler::getBus(), objPath,
119             software::Activation::Activations::Invalid, this);
120         package.close();
121         parser.reset();
122         return -1;
123     }
124 
125     auto deviceUpdaterInfos =
126         associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap,
127                               totalNumComponentUpdates);
128     if (!deviceUpdaterInfos.size())
129     {
130         error(
131             "No matching devices found with the PLDM firmware update package");
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         auto dur =
208             std::chrono::duration<double, std::milli>(endTime - startTime)
209                 .count();
210         error("Firmware update time: {DURATION}ms", "DURATION", dur);
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
295