xref: /openbmc/pldm/fw-update/update_manager.cpp (revision 6a3f9906a774644a8f0ca2978a81b769e948206f)
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                 activation->activation(
245                     software::Activation::Activations::Failed);
246                 return;
247             }
248         }
249 
250         auto endTime = std::chrono::steady_clock::now();
251         auto dur =
252             std::chrono::duration<double, std::milli>(endTime - startTime)
253                 .count();
254         info("Firmware update time: {DURATION}ms", "DURATION", dur);
255         activation->activation(software::Activation::Activations::Active);
256     }
257     return;
258 }
259 
handleRequest(mctp_eid_t eid,uint8_t command,const pldm_msg * request,size_t reqMsgLen)260 Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command,
261                                       const pldm_msg* request, size_t reqMsgLen)
262 {
263     Response response(sizeof(pldm_msg), 0);
264     if (deviceUpdaterMap.contains(eid))
265     {
266         auto search = deviceUpdaterMap.find(eid);
267         if (command == PLDM_REQUEST_FIRMWARE_DATA)
268         {
269             return search->second->requestFwData(request, reqMsgLen);
270         }
271         else if (command == PLDM_TRANSFER_COMPLETE)
272         {
273             return search->second->transferComplete(request, reqMsgLen);
274         }
275         else if (command == PLDM_VERIFY_COMPLETE)
276         {
277             return search->second->verifyComplete(request, reqMsgLen);
278         }
279         else if (command == PLDM_APPLY_COMPLETE)
280         {
281             return search->second->applyComplete(request, reqMsgLen);
282         }
283         else
284         {
285             auto ptr = new (response.data()) pldm_msg;
286             auto rc = encode_cc_only_resp(
287                 request->hdr.instance_id, request->hdr.type,
288                 request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr);
289             assert(rc == PLDM_SUCCESS);
290         }
291     }
292     else
293     {
294         auto ptr = new (response.data()) pldm_msg;
295         auto rc = encode_cc_only_resp(request->hdr.instance_id,
296                                       request->hdr.type, +request->hdr.command,
297                                       PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr);
298         assert(rc == PLDM_SUCCESS);
299     }
300 
301     return response;
302 }
303 
activatePackage()304 void UpdateManager::activatePackage()
305 {
306     startTime = std::chrono::steady_clock::now();
307     for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap)
308     {
309         deviceUpdaterPtr->startFwUpdateFlow();
310     }
311 }
312 
clearActivationInfo()313 void UpdateManager::clearActivationInfo()
314 {
315     activation.reset();
316     activationProgress.reset();
317     objPath.clear();
318 
319     if (package.is_open())
320     {
321         package.close();
322     }
323     deviceUpdaterMap.clear();
324     deviceUpdateCompletionMap.clear();
325     parser.reset();
326     std::filesystem::remove(fwPackageFilePath);
327     totalNumComponentUpdates = 0;
328     compUpdateCompletedCount = 0;
329 }
330 
updateActivationProgress()331 void UpdateManager::updateActivationProgress()
332 {
333     compUpdateCompletedCount++;
334     auto progressPercent = static_cast<uint8_t>(std::floor(
335         (100 * compUpdateCompletedCount) / totalNumComponentUpdates));
336     activationProgress->progress(progressPercent);
337 }
338 
339 } // namespace fw_update
340 
341 } // namespace pldm
342