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