xref: /openbmc/pldm/fw-update/update_manager.cpp (revision f357b503aa7197c51e96e3e0946e14cdd964a0d4)
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 
processPackage(const std::filesystem::path & packageFilePath)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", errno, "FILE", packageFilePath);
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(packageSize);
80     package.read(reinterpret_cast<char*>(packageHeader.data()), packageSize);
81 
82     parser = parsePkgHeader(packageHeader);
83     if (parser == nullptr)
84     {
85         error("Invalid PLDM package header information");
86         package.close();
87         std::filesystem::remove(packageFilePath);
88         return -1;
89     }
90 
91     // Populate object path with the hash of the package version
92     size_t versionHash = std::hash<std::string>{}(parser->pkgVersion);
93     objPath = swRootPath + std::to_string(versionHash);
94 
95     package.seekg(0);
96     try
97     {
98         parser->parse(packageHeader, packageSize);
99     }
100     catch (const std::exception& e)
101     {
102         error("Invalid PLDM package header, error - {ERROR}", "ERROR", e);
103         activation = std::make_unique<Activation>(
104             pldm::utils::DBusHandler::getBus(), objPath,
105             software::Activation::Activations::Invalid, this);
106         package.close();
107         parser.reset();
108         return -1;
109     }
110 
111     auto deviceUpdaterInfos =
112         associatePkgToDevices(parser->getFwDeviceIDRecords(), descriptorMap,
113                               totalNumComponentUpdates);
114     if (!deviceUpdaterInfos.size())
115     {
116         error(
117             "No matching devices found with the PLDM firmware update package");
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 0;
124     }
125 
126     const auto& fwDeviceIDRecords = parser->getFwDeviceIDRecords();
127     const auto& compImageInfos = parser->getComponentImageInfos();
128 
129     for (const auto& deviceUpdaterInfo : deviceUpdaterInfos)
130     {
131         const auto& fwDeviceIDRecord =
132             fwDeviceIDRecords[deviceUpdaterInfo.second];
133         auto search = componentInfoMap.find(deviceUpdaterInfo.first);
134         deviceUpdaterMap.emplace(
135             deviceUpdaterInfo.first,
136             std::make_unique<DeviceUpdater>(
137                 deviceUpdaterInfo.first, package, fwDeviceIDRecord,
138                 compImageInfos, search->second, MAXIMUM_TRANSFER_SIZE, this));
139     }
140 
141     fwPackageFilePath = packageFilePath;
142     activation = std::make_unique<Activation>(
143         pldm::utils::DBusHandler::getBus(), objPath,
144         software::Activation::Activations::Ready, this);
145     activationProgress = std::make_unique<ActivationProgress>(
146         pldm::utils::DBusHandler::getBus(), objPath);
147 
148     return 0;
149 }
150 
associatePkgToDevices(const FirmwareDeviceIDRecords & fwDeviceIDRecords,const DescriptorMap & descriptorMap,TotalComponentUpdates & totalNumComponentUpdates)151 DeviceUpdaterInfos UpdateManager::associatePkgToDevices(
152     const FirmwareDeviceIDRecords& fwDeviceIDRecords,
153     const DescriptorMap& descriptorMap,
154     TotalComponentUpdates& totalNumComponentUpdates)
155 {
156     DeviceUpdaterInfos deviceUpdaterInfos;
157     for (size_t index = 0; index < fwDeviceIDRecords.size(); ++index)
158     {
159         const auto& deviceIDDescriptors =
160             std::get<Descriptors>(fwDeviceIDRecords[index]);
161         for (const auto& [eid, descriptors] : descriptorMap)
162         {
163             if (std::includes(descriptors.begin(), descriptors.end(),
164                               deviceIDDescriptors.begin(),
165                               deviceIDDescriptors.end()))
166             {
167                 deviceUpdaterInfos.emplace_back(std::make_pair(eid, index));
168                 const auto& applicableComponents =
169                     std::get<ApplicableComponents>(fwDeviceIDRecords[index]);
170                 totalNumComponentUpdates += applicableComponents.size();
171             }
172         }
173     }
174     return deviceUpdaterInfos;
175 }
176 
updateDeviceCompletion(mctp_eid_t eid,bool status)177 void UpdateManager::updateDeviceCompletion(mctp_eid_t eid, bool status)
178 {
179     deviceUpdateCompletionMap.emplace(eid, status);
180     if (deviceUpdateCompletionMap.size() == deviceUpdaterMap.size())
181     {
182         for (const auto& [eid, status] : deviceUpdateCompletionMap)
183         {
184             if (!status)
185             {
186                 activation->activation(
187                     software::Activation::Activations::Failed);
188                 return;
189             }
190         }
191 
192         auto endTime = std::chrono::steady_clock::now();
193         auto dur =
194             std::chrono::duration<double, std::milli>(endTime - startTime)
195                 .count();
196         info("Firmware update time: {DURATION}ms", "DURATION", dur);
197         activation->activation(software::Activation::Activations::Active);
198     }
199     return;
200 }
201 
handleRequest(mctp_eid_t eid,uint8_t command,const pldm_msg * request,size_t reqMsgLen)202 Response UpdateManager::handleRequest(mctp_eid_t eid, uint8_t command,
203                                       const pldm_msg* request, size_t reqMsgLen)
204 {
205     Response response(sizeof(pldm_msg), 0);
206     if (deviceUpdaterMap.contains(eid))
207     {
208         auto search = deviceUpdaterMap.find(eid);
209         if (command == PLDM_REQUEST_FIRMWARE_DATA)
210         {
211             return search->second->requestFwData(request, reqMsgLen);
212         }
213         else if (command == PLDM_TRANSFER_COMPLETE)
214         {
215             return search->second->transferComplete(request, reqMsgLen);
216         }
217         else if (command == PLDM_VERIFY_COMPLETE)
218         {
219             return search->second->verifyComplete(request, reqMsgLen);
220         }
221         else if (command == PLDM_APPLY_COMPLETE)
222         {
223             return search->second->applyComplete(request, reqMsgLen);
224         }
225         else
226         {
227             auto ptr = new (response.data()) pldm_msg;
228             auto rc = encode_cc_only_resp(
229                 request->hdr.instance_id, request->hdr.type,
230                 request->hdr.command, PLDM_ERROR_INVALID_DATA, ptr);
231             assert(rc == PLDM_SUCCESS);
232         }
233     }
234     else
235     {
236         auto ptr = new (response.data()) pldm_msg;
237         auto rc = encode_cc_only_resp(request->hdr.instance_id,
238                                       request->hdr.type, +request->hdr.command,
239                                       PLDM_FWUP_COMMAND_NOT_EXPECTED, ptr);
240         assert(rc == PLDM_SUCCESS);
241     }
242 
243     return response;
244 }
245 
activatePackage()246 void UpdateManager::activatePackage()
247 {
248     startTime = std::chrono::steady_clock::now();
249     for (const auto& [eid, deviceUpdaterPtr] : deviceUpdaterMap)
250     {
251         deviceUpdaterPtr->startFwUpdateFlow();
252     }
253 }
254 
clearActivationInfo()255 void UpdateManager::clearActivationInfo()
256 {
257     activation.reset();
258     activationProgress.reset();
259     objPath.clear();
260 
261     deviceUpdaterMap.clear();
262     deviceUpdateCompletionMap.clear();
263     parser.reset();
264     package.close();
265     std::filesystem::remove(fwPackageFilePath);
266     totalNumComponentUpdates = 0;
267     compUpdateCompletedCount = 0;
268 }
269 
updateActivationProgress()270 void UpdateManager::updateActivationProgress()
271 {
272     compUpdateCompletedCount++;
273     auto progressPercent = static_cast<uint8_t>(std::floor(
274         (100 * compUpdateCompletedCount) / totalNumComponentUpdates));
275     activationProgress->progress(progressPercent);
276 }
277 
278 } // namespace fw_update
279 
280 } // namespace pldm
281