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