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