xref: /openbmc/pldm/fw-update/update_manager.cpp (revision 3124782739408b77dbf7df07833336ee63dcc239)
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 #include <sdeventplus/source/event.hpp>
9 
10 #include <cassert>
11 #include <cmath>
12 #include <filesystem>
13 #include <fstream>
14 #include <string>
15 
16 PHOSPHOR_LOG2_USING;
17 
18 namespace pldm
19 {
20 
21 namespace fw_update
22 {
23 
24 namespace fs = std::filesystem;
25 namespace software = sdbusplus::xyz::openbmc_project::Software::server;
26 
getSwId()27 std::string UpdateManager::getSwId()
28 {
29     return std::to_string(
30         std::chrono::duration_cast<std::chrono::seconds>(
31             std::chrono::system_clock::now().time_since_epoch())
32             .count());
33 }
34 
processPackage(const std::filesystem::path & packageFilePath)35 int UpdateManager::processPackage(const std::filesystem::path& packageFilePath)
36 {
37     // If no devices discovered, take no action on the package.
38     if (!descriptorMap.size())
39     {
40         return 0;
41     }
42 
43     namespace software = sdbusplus::xyz::openbmc_project::Software::server;
44     // If a firmware activation of a package is in progress, don't proceed with
45     // package processing
46     if (activation)
47     {
48         if (activation->activation() ==
49             software::Activation::Activations::Activating)
50         {
51             error(
52                 "Activation of PLDM fw update package for version '{VERSION}' already in progress.",
53                 "VERSION", parser->pkgVersion);
54             std::filesystem::remove(packageFilePath);
55             return -1;
56         }
57         else
58         {
59             clearActivationInfo();
60         }
61     }
62 
63     package.open(packageFilePath,
64                  std::ios::binary | std::ios::in | std::ios::ate);
65     if (!package.good())
66     {
67         error(
68             "Failed to open the PLDM fw update package file '{FILE}', error - {ERROR}.",
69             "ERROR", errno, "FILE", packageFilePath);
70         package.close();
71         std::filesystem::remove(packageFilePath);
72         return -1;
73     }
74 
75     uintmax_t packageSize = package.tellg();
76 
77     auto swId = getSwId();
78     objPath = swRootPath + swId;
79 
80     fwPackageFilePath = packageFilePath;
81 
82     try
83     {
84         processStream(package, packageSize);
85         return 0;
86     }
87     catch (sdbusplus::exception_t& e)
88     {
89         error("Exception occurred while processing the package: {ERROR}",
90               "ERROR", e);
91         package.close();
92         std::filesystem::remove(packageFilePath);
93         return -1;
94     }
95 }
96 
processStreamDefer(std::istream & package,uintmax_t packageSize)97 std::string UpdateManager::processStreamDefer(std::istream& package,
98                                               uintmax_t packageSize)
99 {
100     auto swId = getSwId();
101     objPath = swRootPath + swId;
102 
103     // If no devices discovered, take no action on the package.
104     if (!descriptorMap.size())
105     {
106         error(
107             "No devices discovered, cannot process the PLDM fw update package.");
108         throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
109     }
110 
111     updateDeferHandler = std::make_unique<sdeventplus::source::Defer>(
112         event, [this, &package, packageSize](sdeventplus::source::EventBase&) {
113             this->processStream(package, packageSize);
114         });
115 
116     return objPath;
117 }
118 
processStream(std::istream & package,uintmax_t packageSize)119 void UpdateManager::processStream(std::istream& package, uintmax_t packageSize)
120 {
121     startTime = std::chrono::steady_clock::now();
122     if (packageSize < sizeof(pldm_package_header_information))
123     {
124         error(
125             "PLDM fw update package length {SIZE} less than the length of the package header information '{PACKAGE_HEADER_INFO_SIZE}'.",
126             "SIZE", packageSize, "PACKAGE_HEADER_INFO_SIZE",
127             sizeof(pldm_package_header_information));
128         activation = std::make_unique<Activation>(
129             pldm::utils::DBusHandler::getBus(), objPath,
130             software::Activation::Activations::Invalid, this);
131         parser.reset();
132         throw sdbusplus::error::xyz::openbmc_project::software::update::
133             InvalidImage();
134     }
135 
136     package.seekg(0);
137     std::vector<uint8_t> packageHeader(packageSize);
138     package.read(reinterpret_cast<char*>(packageHeader.data()), packageSize);
139 
140     parser = parsePkgHeader(packageHeader);
141     if (parser == nullptr)
142     {
143         error("Invalid PLDM package header information");
144         activation = std::make_unique<Activation>(
145             pldm::utils::DBusHandler::getBus(), objPath,
146             software::Activation::Activations::Invalid, this);
147         parser.reset();
148         throw sdbusplus::error::xyz::openbmc_project::software::update::
149             InvalidImage();
150     }
151 
152     package.seekg(0);
153     try
154     {
155         parser->parse(packageHeader, packageSize);
156     }
157     catch (const std::exception& e)
158     {
159         error("Invalid PLDM package header, error - {ERROR}", "ERROR", e);
160         activation = std::make_unique<Activation>(
161             pldm::utils::DBusHandler::getBus(), objPath,
162             software::Activation::Activations::Invalid, this);
163         parser.reset();
164         throw sdbusplus::error::xyz::openbmc_project::software::update::
165             InvalidImage();
166     }
167 
168     auto deviceUpdaterInfos =
169         associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap,
170                               totalNumComponentUpdates);
171     if (!deviceUpdaterInfos.size())
172     {
173         error(
174             "No matching devices found with the PLDM firmware update package");
175         activation = std::make_unique<Activation>(
176             pldm::utils::DBusHandler::getBus(), objPath,
177             software::Activation::Activations::Invalid, this);
178         parser.reset();
179         throw sdbusplus::error::xyz::openbmc_project::software::update::
180             Incompatible();
181     }
182 
183     const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords();
184     const auto& compImageInfos = parser->getComponentImageInfos();
185 
186     for (const auto& deviceUpdaterInfo : deviceUpdaterInfos)
187     {
188         const auto& fwDeviceIDRecord =
189             fwDeviceIDRecords[deviceUpdaterInfo.second];
190         auto search = componentInfoMap.find(deviceUpdaterInfo.first);
191         deviceUpdaterMap.emplace(
192             deviceUpdaterInfo.first,
193             std::make_unique<DeviceUpdater>(
194                 deviceUpdaterInfo.first, package, fwDeviceIDRecord,
195                 compImageInfos, search->second, MAXIMUM_TRANSFER_SIZE, this));
196     }
197 
198     activation = std::make_unique<Activation>(
199         pldm::utils::DBusHandler::getBus(), objPath,
200         software::Activation::Activations::Ready, this);
201     activationProgress = std::make_unique<ActivationProgress>(
202         pldm::utils::DBusHandler::getBus(), objPath);
203 
204 #ifndef FW_UPDATE_INOTIFY_ENABLED
205     activation->activation(software::Activation::Activations::Activating);
206 #endif
207 }
208 
associatePkgToDevices(const FirmwareDeviceIDRecords & fwDeviceIDRecords,const DescriptorMap & descriptorMap,TotalComponentUpdates & totalNumComponentUpdates)209 DeviceUpdaterInfos UpdateManager::associatePkgToDevices(
210     const FirmwareDeviceIDRecords& fwDeviceIDRecords,
211     const DescriptorMap& descriptorMap,
212     TotalComponentUpdates& totalNumComponentUpdates)
213 {
214     DeviceUpdaterInfos deviceUpdaterInfos;
215     for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index)
216     {
217         const auto& deviceIDDescriptors =
218             std::get<Descriptors>(fwDeviceIDRecords[index]);
219         for (const auto& [eid, descriptors] : descriptorMap)
220         {
221             if (std::includes(descriptors.begin(), descriptors.end(),
222                               deviceIDDescriptors.begin(),
223                               deviceIDDescriptors.end()))
224             {
225                 deviceUpdaterInfos.emplace_back(std::make_pair(eid, index));
226                 const auto& applicableComponents =
227                     std::get<ApplicableComponents>(fwDeviceIDRecords[index]);
228                 totalNumComponentUpdates += applicableComponents.size();
229             }
230         }
231     }
232     return deviceUpdaterInfos;
233 }
234 
updateDeviceCompletion(mctp_eid_t eid,bool status)235 void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status)
236 {
237     deviceUpdateCompletionMap.emplace(eid, status);
238     if (deviceUpdateCompletionMap.size() == deviceUpdaterMap.size())
239     {
240         for (const auto& [eid, status] : deviceUpdateCompletionMap)
241         {
242             if (!status)
243             {
244                 info("Firmware update failed on eid {EID}", "EID", eid);
245                 activation->activation(
246                     software::Activation::Activations::Failed);
247                 return;
248             }
249         }
250 
251         auto endTime = std::chrono::steady_clock::now();
252         auto dur =
253             std::chrono::duration<double, std::milli>(endTime - startTime)
254                 .count();
255         info("Firmware update time: {DURATION}ms", "DURATION", dur);
256         activation->activation(software::Activation::Activations::Active);
257     }
258     return;
259 }
260 
handleRequest(mctp_eid_t eid,uint8_t command,const pldm_msg * request,size_t reqMsgLen)261 Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command,
262                                       const pldm_msg* request, size_t reqMsgLen)
263 {
264     Response response(sizeof(pldm_msg), 0);
265     if (deviceUpdaterMap.contains(eid))
266     {
267         auto search = deviceUpdaterMap.find(eid);
268         if (command == PLDM_REQUEST_FIRMWARE_DATA)
269         {
270             return search->second->requestFwData(request, reqMsgLen);
271         }
272         else if (command == PLDM_TRANSFER_COMPLETE)
273         {
274             return search->second->transferComplete(request, reqMsgLen);
275         }
276         else if (command == PLDM_VERIFY_COMPLETE)
277         {
278             return search->second->verifyComplete(request, reqMsgLen);
279         }
280         else if (command == PLDM_APPLY_COMPLETE)
281         {
282             return search->second->applyComplete(request, reqMsgLen);
283         }
284         else
285         {
286             auto ptr = new (response.data()) pldm_msg;
287             auto rc = encode_cc_only_resp(
288                 request->hdr.instance_id, request->hdr.type,
289                 request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr);
290             assert(rc == PLDM_SUCCESS);
291         }
292     }
293     else
294     {
295         auto ptr = new (response.data()) pldm_msg;
296         auto rc = encode_cc_only_resp(request->hdr.instance_id,
297                                       request->hdr.type, +request->hdr.command,
298                                       PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr);
299         assert(rc == PLDM_SUCCESS);
300     }
301 
302     return response;
303 }
304 
activatePackage()305 void UpdateManager::activatePackage()
306 {
307     startTime = std::chrono::steady_clock::now();
308     for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap)
309     {
310         deviceUpdaterPtr->startFwUpdateFlow();
311     }
312 }
313 
clearActivationInfo()314 void UpdateManager::clearActivationInfo()
315 {
316     activation.reset();
317     activationProgress.reset();
318     objPath.clear();
319 
320     if (package.is_open())
321     {
322         package.close();
323     }
324     deviceUpdaterMap.clear();
325     deviceUpdateCompletionMap.clear();
326     parser.reset();
327     std::filesystem::remove(fwPackageFilePath);
328     totalNumComponentUpdates = 0;
329 }
330 
updateActivationProgress()331 void UpdateManager::updateActivationProgress()
332 {
333     using mapEl = std::pair<const mctp_eid_t, std::unique_ptr<DeviceUpdater>>;
334     auto min = std::ranges::min_element(
335         deviceUpdaterMap, [](const mapEl& lhs, const mapEl& rhs) {
336             return lhs.second->getProgress() < rhs.second->getProgress();
337         });
338 
339     if (min == deviceUpdaterMap.end())
340     {
341         return;
342     }
343 
344     uint8_t progress = min->second->getProgress();
345     if (progress != lastProgress)
346     {
347         activationProgress->progress(progress);
348         lastProgress = progress;
349     }
350 }
351 
352 } // namespace fw_update
353 
354 } // namespace pldm
355