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