xref: /openbmc/pldm/fw-update/device_updater.cpp (revision 3124782739408b77dbf7df07833336ee63dcc239)
1 #include "device_updater.hpp"
2 
3 #include "activation.hpp"
4 #include "update_manager.hpp"
5 
6 #include <libpldm/firmware_update.h>
7 
8 #include <phosphor-logging/lg2.hpp>
9 
10 #include <functional>
11 
12 PHOSPHOR_LOG2_USING;
13 
14 namespace pldm
15 {
16 
17 namespace fw_update
18 {
19 
20 // The highest percentage that component transfer can give
21 static constexpr uint8_t componentXferProgressPercent = 97;
22 static constexpr uint8_t componentVerifyProgressPercent = 98;
23 static constexpr uint8_t componentApplyProgressPercent = 99;
24 static constexpr uint8_t firmwareActivationProgressPercent = 100;
25 
UpdateProgress(uint32_t totalSize,mctp_eid_t eid)26 UpdateProgress::UpdateProgress(uint32_t totalSize, mctp_eid_t eid) :
27     progress{}, eid{eid}, totalSize{totalSize}, totalUpdated{},
28     currentState{state::Update}
29 {}
30 
getTotalSize() const31 uint32_t UpdateProgress::getTotalSize() const
32 {
33     return totalSize;
34 }
getProgress() const35 uint8_t UpdateProgress::getProgress() const
36 {
37     return progress;
38 }
39 
updateState(state newState)40 void UpdateProgress::updateState(state newState)
41 {
42     switch (newState)
43     {
44         case state::Update:
45             break;
46         case state::Verify:
47             progress = componentVerifyProgressPercent;
48             break;
49         case state::Apply:
50             progress = componentApplyProgressPercent;
51             break;
52         default:
53             warning("Invalid state {STATE} provided", "STATE", newState);
54             return;
55     };
56     currentState = newState;
57 }
58 
reportFwUpdate(uint32_t amountUpdated)59 void UpdateProgress::reportFwUpdate(uint32_t amountUpdated)
60 {
61     if (currentState != state::Update)
62     {
63         error("invalid state when updating progress: eid {EID}", "EID", eid);
64         return;
65     }
66 
67     if (amountUpdated > totalSize)
68     {
69         error("invalid size when updating progress: eid {EID}", "EID", eid);
70         return;
71     }
72 
73     if (amountUpdated + totalUpdated > totalSize)
74     {
75         error("invalid total size when updating progress: eid {EID}", "EID",
76               eid);
77         return;
78     }
79 
80     if (totalSize == 0)
81     {
82         error("invalid package size when updating progress: eid {EID}", "EID",
83               eid);
84         return;
85     }
86 
87     totalUpdated += amountUpdated;
88     progress = static_cast<uint8_t>(std::floor(
89         1.0 * componentXferProgressPercent * totalUpdated / totalSize));
90     return;
91 }
92 
DeviceUpdater(mctp_eid_t eid,std::istream & package,const FirmwareDeviceIDRecord & fwDeviceIDRecord,const ComponentImageInfos & compImageInfos,const ComponentInfo & compInfo,uint32_t maxTransferSize,UpdateManager * updateManager)93 DeviceUpdater::DeviceUpdater(
94     mctp_eid_t eid, std::istream& package,
95     const FirmwareDeviceIDRecord& fwDeviceIDRecord,
96     const ComponentImageInfos& compImageInfos, const ComponentInfo& compInfo,
97     uint32_t maxTransferSize, UpdateManager* updateManager) :
98     eid(eid), package(package), fwDeviceIDRecord(fwDeviceIDRecord),
99     compImageInfos(compImageInfos), compInfo(compInfo),
100     maxTransferSize(maxTransferSize), updateManager(updateManager),
101     activationComplete{false}
102 {
103     const auto& applicableComponents =
104         std::get<ApplicableComponents>(fwDeviceIDRecord);
105     // create as many progress objects as there are components
106     // and initialize them with the size of each component
107     for (const auto& applicableComponent : applicableComponents)
108     {
109         const auto& componentSize =
110             std::get<6>(compImageInfos[applicableComponent]);
111         progress.emplace_back(componentSize, eid);
112     }
113 }
114 
startFwUpdateFlow()115 void DeviceUpdater::startFwUpdateFlow()
116 {
117     auto instanceIdResult = updateManager->instanceIdDb.next(eid);
118     if (!instanceIdResult)
119     {
120         throw pldm::InstanceIdError(instanceIdResult.error());
121     }
122     auto instanceId = instanceIdResult.value();
123     // NumberOfComponents
124     const auto& applicableComponents =
125         std::get<ApplicableComponents>(fwDeviceIDRecord);
126     // PackageDataLength
127     const auto& fwDevicePkgData =
128         std::get<FirmwareDevicePackageData>(fwDeviceIDRecord);
129     // ComponentImageSetVersionString
130     const auto& compImageSetVersion =
131         std::get<ComponentImageSetVersion>(fwDeviceIDRecord);
132     variable_field compImgSetVerStrInfo{};
133     compImgSetVerStrInfo.ptr =
134         reinterpret_cast<const uint8_t*>(compImageSetVersion.data());
135     compImgSetVerStrInfo.length =
136         static_cast<uint8_t>(compImageSetVersion.size());
137 
138     Request request(
139         sizeof(pldm_msg_hdr) + sizeof(struct pldm_request_update_req) +
140         compImgSetVerStrInfo.length);
141     auto requestMsg = new (request.data()) pldm_msg;
142 
143     auto rc = encode_request_update_req(
144         instanceId, maxTransferSize, applicableComponents.size(),
145         PLDM_FWUP_MIN_OUTSTANDING_REQ, fwDevicePkgData.size(),
146         PLDM_STR_TYPE_ASCII, compImgSetVerStrInfo.length, &compImgSetVerStrInfo,
147         requestMsg,
148         sizeof(struct pldm_request_update_req) + compImgSetVerStrInfo.length);
149     if (rc)
150     {
151         // Handle error scenario
152         updateManager->instanceIdDb.free(eid, instanceId);
153         error(
154             "Failed to encode request update request for endpoint ID '{EID}', response code '{RC}'",
155             "EID", eid, "RC", rc);
156     }
157 
158     rc = updateManager->handler.registerRequest(
159         eid, instanceId, PLDM_FWUP, PLDM_REQUEST_UPDATE, std::move(request),
160         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
161             this->requestUpdate(eid, response, respMsgLen);
162         });
163     if (rc)
164     {
165         // Handle error scenario
166         error(
167             "Failed to send request update for endpoint ID '{EID}', response code '{RC}'",
168             "EID", eid, "RC", rc);
169     }
170 }
171 
requestUpdate(mctp_eid_t eid,const pldm_msg * response,size_t respMsgLen)172 void DeviceUpdater::requestUpdate(mctp_eid_t eid, const pldm_msg* response,
173                                   size_t respMsgLen)
174 {
175     if (response == nullptr || !respMsgLen)
176     {
177         // Handle error scenario
178         error("No response received for request update for endpoint ID '{EID}'",
179               "EID", eid);
180         updateManager->updateDeviceCompletion(eid, false);
181         return;
182     }
183 
184     uint8_t completionCode = 0;
185     uint16_t fdMetaDataLen = 0;
186     uint8_t fdWillSendPkgData = 0;
187 
188     auto rc = decode_request_update_resp(response, respMsgLen, &completionCode,
189                                          &fdMetaDataLen, &fdWillSendPkgData);
190     if (rc)
191     {
192         error(
193             "Failed to decode request update response for endpoint ID '{EID}', response code '{RC}'",
194             "EID", eid, "RC", rc);
195         return;
196     }
197     if (completionCode)
198     {
199         error(
200             "Failure in request update response for endpoint ID '{EID}', completion code '{CC}'",
201             "EID", eid, "CC", completionCode);
202         updateManager->updateDeviceCompletion(eid, false);
203         return;
204     }
205 
206     // Optional fields DeviceMetaData and GetPackageData not handled
207     pldmRequest = std::make_unique<sdeventplus::source::Defer>(
208         updateManager->event,
209         std::bind(&DeviceUpdater::sendPassCompTableRequest, this,
210                   componentIndex));
211 }
212 
sendPassCompTableRequest(size_t offset)213 void DeviceUpdater::sendPassCompTableRequest(size_t offset)
214 {
215     pldmRequest.reset();
216 
217     auto instanceIdResult = updateManager->instanceIdDb.next(eid);
218     if (!instanceIdResult)
219     {
220         throw pldm::InstanceIdError(instanceIdResult.error());
221     }
222     auto instanceId = instanceIdResult.value();
223     // TransferFlag
224     const auto& applicableComponents =
225         std::get<ApplicableComponents>(fwDeviceIDRecord);
226     uint8_t transferFlag = 0;
227     if (applicableComponents.size() == 1)
228     {
229         transferFlag = PLDM_START_AND_END;
230     }
231     else if (offset == 0)
232     {
233         transferFlag = PLDM_START;
234     }
235     else if (offset == applicableComponents.size() - 1)
236     {
237         transferFlag = PLDM_END;
238     }
239     else
240     {
241         transferFlag = PLDM_MIDDLE;
242     }
243     const auto& comp = compImageInfos[applicableComponents[offset]];
244     // ComponentClassification
245     CompClassification compClassification = std::get<static_cast<size_t>(
246         ComponentImageInfoPos::CompClassificationPos)>(comp);
247     // ComponentIdentifier
248     CompIdentifier compIdentifier =
249         std::get<static_cast<size_t>(ComponentImageInfoPos::CompIdentifierPos)>(
250             comp);
251     // ComponentClassificationIndex
252     CompClassificationIndex compClassificationIndex{};
253     auto compKey = std::make_pair(compClassification, compIdentifier);
254     if (compInfo.contains(compKey))
255     {
256         auto search = compInfo.find(compKey);
257         compClassificationIndex = search->second;
258     }
259     else
260     {
261         // Handle error scenario
262         error(
263             "Failed to find component classification '{CLASSIFICATION}' and identifier '{IDENTIFIER}'",
264             "CLASSIFICATION", compClassification, "IDENTIFIER", compIdentifier);
265     }
266     // ComponentComparisonStamp
267     CompComparisonStamp compComparisonStamp = std::get<static_cast<size_t>(
268         ComponentImageInfoPos::CompComparisonStampPos)>(comp);
269     // ComponentVersionString
270     const auto& compVersion =
271         std::get<static_cast<size_t>(ComponentImageInfoPos::CompVersionPos)>(
272             comp);
273     variable_field compVerStrInfo{};
274     compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVersion.data());
275     compVerStrInfo.length = static_cast<uint8_t>(compVersion.size());
276 
277     Request request(
278         sizeof(pldm_msg_hdr) + sizeof(struct pldm_pass_component_table_req) +
279         compVerStrInfo.length);
280     auto requestMsg = new (request.data()) pldm_msg;
281     auto rc = encode_pass_component_table_req(
282         instanceId, transferFlag, compClassification, compIdentifier,
283         compClassificationIndex, compComparisonStamp, PLDM_STR_TYPE_ASCII,
284         compVerStrInfo.length, &compVerStrInfo, requestMsg,
285         sizeof(pldm_pass_component_table_req) + compVerStrInfo.length);
286     if (rc)
287     {
288         // Handle error scenario
289         updateManager->instanceIdDb.free(eid, instanceId);
290         error(
291             "Failed to encode pass component table req for endpoint ID '{EID}', response code '{RC}'",
292             "EID", eid, "RC", rc);
293     }
294 
295     rc = updateManager->handler.registerRequest(
296         eid, instanceId, PLDM_FWUP, PLDM_PASS_COMPONENT_TABLE,
297         std::move(request),
298         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
299             this->passCompTable(eid, response, respMsgLen);
300         });
301     if (rc)
302     {
303         // Handle error scenario
304         error(
305             "Failed to send pass component table request for endpoint ID '{EID}', response code '{RC}'",
306             "EID", eid, "RC", rc);
307     }
308 }
309 
passCompTable(mctp_eid_t eid,const pldm_msg * response,size_t respMsgLen)310 void DeviceUpdater::passCompTable(mctp_eid_t eid, const pldm_msg* response,
311                                   size_t respMsgLen)
312 {
313     if (response == nullptr || !respMsgLen)
314     {
315         // Handle error scenario
316         error(
317             "No response received for pass component table for endpoint ID '{EID}'",
318             "EID", eid);
319         updateManager->updateDeviceCompletion(eid, false);
320         return;
321     }
322 
323     uint8_t completionCode = 0;
324     uint8_t compResponse = 0;
325     uint8_t compResponseCode = 0;
326 
327     auto rc =
328         decode_pass_component_table_resp(response, respMsgLen, &completionCode,
329                                          &compResponse, &compResponseCode);
330     if (rc)
331     {
332         // Handle error scenario
333         error(
334             "Failed to decode pass component table response for endpoint ID '{EID}', response code '{RC}'",
335             "EID", eid, "RC", rc);
336         return;
337     }
338     if (completionCode)
339     {
340         // Handle error scenario
341         error(
342             "Failed to pass component table response for endpoint ID '{EID}', completion code '{CC}'",
343             "EID", eid, "CC", completionCode);
344         updateManager->updateDeviceCompletion(eid, false);
345         return;
346     }
347     // Handle ComponentResponseCode
348 
349     const auto& applicableComponents =
350         std::get<ApplicableComponents>(fwDeviceIDRecord);
351     if (componentIndex == applicableComponents.size() - 1)
352     {
353         componentIndex = 0;
354         pldmRequest = std::make_unique<sdeventplus::source::Defer>(
355             updateManager->event,
356             std::bind(&DeviceUpdater::sendUpdateComponentRequest, this,
357                       componentIndex));
358     }
359     else
360     {
361         componentIndex++;
362         pldmRequest = std::make_unique<sdeventplus::source::Defer>(
363             updateManager->event,
364             std::bind(&DeviceUpdater::sendPassCompTableRequest, this,
365                       componentIndex));
366     }
367 }
368 
sendUpdateComponentRequest(size_t offset)369 void DeviceUpdater::sendUpdateComponentRequest(size_t offset)
370 {
371     pldmRequest.reset();
372     auto instanceIdResult = updateManager->instanceIdDb.next(eid);
373     if (!instanceIdResult)
374     {
375         throw pldm::InstanceIdError(instanceIdResult.error());
376     }
377     auto instanceId = instanceIdResult.value();
378     const auto& applicableComponents =
379         std::get<ApplicableComponents>(fwDeviceIDRecord);
380     const auto& comp = compImageInfos[applicableComponents[offset]];
381     // ComponentClassification
382     CompClassification compClassification = std::get<static_cast<size_t>(
383         ComponentImageInfoPos::CompClassificationPos)>(comp);
384     // ComponentIdentifier
385     CompIdentifier compIdentifier =
386         std::get<static_cast<size_t>(ComponentImageInfoPos::CompIdentifierPos)>(
387             comp);
388     // ComponentClassificationIndex
389     CompClassificationIndex compClassificationIndex{};
390     auto compKey = std::make_pair(compClassification, compIdentifier);
391     if (compInfo.contains(compKey))
392     {
393         auto search = compInfo.find(compKey);
394         compClassificationIndex = search->second;
395     }
396     else
397     {
398         // Handle error scenario
399         error(
400             "Failed to find component classification '{CLASSIFICATION}' and identifier '{IDENTIFIER}'",
401             "CLASSIFICATION", compClassification, "IDENTIFIER", compIdentifier);
402     }
403 
404     // UpdateOptionFlags
405     bitfield32_t updateOptionFlags;
406     updateOptionFlags.bits.bit0 = std::get<3>(comp)[0];
407     // ComponentVersion
408     const auto& compVersion = std::get<7>(comp);
409     variable_field compVerStrInfo{};
410     compVerStrInfo.ptr = reinterpret_cast<const uint8_t*>(compVersion.data());
411     compVerStrInfo.length = static_cast<uint8_t>(compVersion.size());
412 
413     Request request(
414         sizeof(pldm_msg_hdr) + sizeof(struct pldm_update_component_req) +
415         compVerStrInfo.length);
416     auto requestMsg = new (request.data()) pldm_msg;
417 
418     auto rc = encode_update_component_req(
419         instanceId, compClassification, compIdentifier, compClassificationIndex,
420         std::get<static_cast<size_t>(
421             ComponentImageInfoPos::CompComparisonStampPos)>(comp),
422         std::get<static_cast<size_t>(ComponentImageInfoPos::CompSizePos)>(comp),
423         updateOptionFlags, PLDM_STR_TYPE_ASCII, compVerStrInfo.length,
424         &compVerStrInfo, requestMsg,
425         sizeof(pldm_update_component_req) + compVerStrInfo.length);
426     if (rc)
427     {
428         // Handle error scenario
429         updateManager->instanceIdDb.free(eid, instanceId);
430         error(
431             "Failed to encode update component req for endpoint ID '{EID}', response code '{RC}'",
432             "EID", eid, "RC", rc);
433     }
434 
435     rc = updateManager->handler.registerRequest(
436         eid, instanceId, PLDM_FWUP, PLDM_UPDATE_COMPONENT, std::move(request),
437         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
438             this->updateComponent(eid, response, respMsgLen);
439         });
440     if (rc)
441     {
442         // Handle error scenario
443         error(
444             "Failed to send update request for endpoint ID '{EID}', response code '{RC}'",
445             "EID", eid, "RC", rc);
446     }
447 }
448 
updateComponent(mctp_eid_t eid,const pldm_msg * response,size_t respMsgLen)449 void DeviceUpdater::updateComponent(mctp_eid_t eid, const pldm_msg* response,
450                                     size_t respMsgLen)
451 {
452     if (response == nullptr || !respMsgLen)
453     {
454         // Handle error scenario
455         error(
456             "No response received for update component with endpoint ID {EID}",
457             "EID", eid);
458         updateManager->updateDeviceCompletion(eid, false);
459         return;
460     }
461 
462     uint8_t completionCode = 0;
463     uint8_t compCompatibilityResp = 0;
464     uint8_t compCompatibilityRespCode = 0;
465     bitfield32_t updateOptionFlagsEnabled{};
466     uint16_t timeBeforeReqFWData = 0;
467 
468     auto rc = decode_update_component_resp(
469         response, respMsgLen, &completionCode, &compCompatibilityResp,
470         &compCompatibilityRespCode, &updateOptionFlagsEnabled,
471         &timeBeforeReqFWData);
472     if (rc)
473     {
474         error(
475             "Failed to decode update request response for endpoint ID '{EID}', response code '{RC}'",
476             "EID", eid, "RC", rc);
477         return;
478     }
479     if (completionCode)
480     {
481         error(
482             "Failed to update request response for endpoint ID '{EID}', completion code '{CC}'",
483             "EID", eid, "CC", completionCode);
484         return;
485         updateManager->updateDeviceCompletion(eid, false);
486     }
487 }
488 
createRequestFwDataTimer()489 void DeviceUpdater::createRequestFwDataTimer()
490 {
491     reqFwDataTimer = std::make_unique<sdbusplus::Timer>([this]() -> void {
492         componentUpdateStatus[componentIndex] = false;
493         sendCancelUpdateComponentRequest();
494         updateManager->updateDeviceCompletion(eid, false);
495     });
496 }
497 
requestFwData(const pldm_msg * request,size_t payloadLength)498 Response DeviceUpdater::requestFwData(const pldm_msg* request,
499                                       size_t payloadLength)
500 {
501     uint8_t completionCode = PLDM_SUCCESS;
502     uint32_t offset = 0;
503     uint32_t length = 0;
504     Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0);
505     auto responseMsg = new (response.data()) pldm_msg;
506     auto rc = decode_request_firmware_data_req(request, payloadLength, &offset,
507                                                &length);
508     if (rc)
509     {
510         error(
511             "Failed to decode request firmware date request for endpoint ID '{EID}', response code '{RC}'",
512             "EID", eid, "RC", rc);
513         rc = encode_request_firmware_data_resp(
514             request->hdr.instance_id, PLDM_ERROR_INVALID_DATA, responseMsg,
515             sizeof(completionCode));
516         if (rc)
517         {
518             error(
519                 "Failed to encode request firmware date response for endpoint ID '{EID}', response code '{RC}'",
520                 "EID", eid, "RC", rc);
521         }
522         return response;
523     }
524 
525     const auto& applicableComponents =
526         std::get<ApplicableComponents>(fwDeviceIDRecord);
527     const auto& comp = compImageInfos[applicableComponents[componentIndex]];
528     auto compOffset = std::get<5>(comp);
529     auto compSize = std::get<6>(comp);
530     debug("Decoded fw request data at offset '{OFFSET}' and length '{LENGTH}' ",
531           "OFFSET", offset, "LENGTH", length);
532     if (length < PLDM_FWUP_BASELINE_TRANSFER_SIZE || length > maxTransferSize)
533     {
534         rc = encode_request_firmware_data_resp(
535             request->hdr.instance_id, PLDM_FWUP_INVALID_TRANSFER_LENGTH,
536             responseMsg, sizeof(completionCode));
537         if (rc)
538         {
539             error(
540                 "Failed to encode request firmware date response for endpoint ID '{EID}', response code '{RC}'",
541                 "EID", eid, "RC", rc);
542         }
543         return response;
544     }
545 
546     if (offset + length > compSize + PLDM_FWUP_BASELINE_TRANSFER_SIZE)
547     {
548         rc = encode_request_firmware_data_resp(
549             request->hdr.instance_id, PLDM_FWUP_DATA_OUT_OF_RANGE, responseMsg,
550             sizeof(completionCode));
551         if (rc)
552         {
553             error(
554                 "Failed to encode request firmware date response for endpoint ID '{EID}', response code '{RC}'",
555                 "EID", eid, "RC", rc);
556         }
557         return response;
558     }
559 
560     size_t padBytes = 0;
561     if (offset + length > compSize)
562     {
563         padBytes = offset + length - compSize;
564     }
565 
566     if (componentIndex < progress.size())
567     {
568         // technically this should never happen, as its an invariant
569         // but check to prefer a failed fw update over a pldm crash
570         progress[componentIndex].reportFwUpdate(length);
571         if (updateManager != nullptr)
572         {
573             updateManager->updateActivationProgress();
574         }
575     }
576 
577     response.resize(sizeof(pldm_msg_hdr) + sizeof(completionCode) + length);
578     responseMsg = new (response.data()) pldm_msg;
579     package.seekg(compOffset + offset);
580     package.read(
581         reinterpret_cast<char*>(
582             response.data() + sizeof(pldm_msg_hdr) + sizeof(completionCode)),
583         length - padBytes);
584     rc = encode_request_firmware_data_resp(
585         request->hdr.instance_id, completionCode, responseMsg,
586         sizeof(completionCode));
587     if (rc)
588     {
589         error(
590             "Failed to encode request firmware date response for endpoint ID '{EID}', response code '{RC}'",
591             "EID", eid, "RC", rc);
592         return response;
593     }
594 
595     if (!reqFwDataTimer)
596     {
597         if (offset != 0)
598         {
599             warning("First data request is not at offset 0");
600         }
601         createRequestFwDataTimer();
602     }
603 
604     if (reqFwDataTimer)
605     {
606         reqFwDataTimer->start(std::chrono::seconds(updateTimeoutSeconds),
607                               false);
608     }
609     else
610     {
611         error(
612             "Failed to start timer for handling request firmware data for endpoint ID {EID}",
613             "EID", eid, "RC", rc);
614     }
615 
616     return response;
617 }
618 
transferComplete(const pldm_msg * request,size_t payloadLength)619 Response DeviceUpdater::transferComplete(const pldm_msg* request,
620                                          size_t payloadLength)
621 {
622     uint8_t completionCode = PLDM_SUCCESS;
623     Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0);
624     auto responseMsg = new (response.data()) pldm_msg;
625 
626     if (reqFwDataTimer)
627     {
628         reqFwDataTimer->stop();
629         reqFwDataTimer.reset();
630     }
631 
632     uint8_t transferResult = 0;
633     auto rc =
634         decode_transfer_complete_req(request, payloadLength, &transferResult);
635     if (rc)
636     {
637         error(
638             "Failed to decode TransferComplete request for endpoint ID '{EID}', response code '{RC}'",
639             "EID", eid, "RC", rc);
640         rc = encode_transfer_complete_resp(request->hdr.instance_id,
641                                            PLDM_ERROR_INVALID_DATA, responseMsg,
642                                            sizeof(completionCode));
643         if (rc)
644         {
645             error(
646                 "Failed to encode TransferComplete response for endpoint ID '{EID}', response code '{RC}'",
647                 "EID", eid, "RC", rc);
648         }
649         return response;
650     }
651 
652     const auto& applicableComponents =
653         std::get<ApplicableComponents>(fwDeviceIDRecord);
654     const auto& comp = compImageInfos[applicableComponents[componentIndex]];
655     const auto& compVersion = std::get<7>(comp);
656 
657     if (transferResult == PLDM_FWUP_TRANSFER_SUCCESS)
658     {
659         info(
660             "Component endpoint ID '{EID}' and version '{COMPONENT_VERSION}' transfer complete.",
661             "EID", eid, "COMPONENT_VERSION", compVersion);
662     }
663     else
664     {
665         error(
666             "Failure in transfer of the component endpoint ID '{EID}' and version '{COMPONENT_VERSION}' with transfer result - {RESULT}",
667             "EID", eid, "COMPONENT_VERSION", compVersion, "RESULT",
668             transferResult);
669         updateManager->updateDeviceCompletion(eid, false);
670         componentUpdateStatus[componentIndex] = false;
671         sendCancelUpdateComponentRequest();
672     }
673 
674     rc = encode_transfer_complete_resp(request->hdr.instance_id, completionCode,
675                                        responseMsg, sizeof(completionCode));
676     if (rc)
677     {
678         error(
679             "Failed to encode transfer complete response of endpoint ID '{EID}', response code '{RC}'",
680             "EID", eid, "RC", rc);
681         return response;
682     }
683 
684     return response;
685 }
686 
verifyComplete(const pldm_msg * request,size_t payloadLength)687 Response DeviceUpdater::verifyComplete(const pldm_msg* request,
688                                        size_t payloadLength)
689 {
690     uint8_t completionCode = PLDM_SUCCESS;
691     Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0);
692     auto responseMsg = new (response.data()) pldm_msg;
693 
694     uint8_t verifyResult = 0;
695     auto rc = decode_verify_complete_req(request, payloadLength, &verifyResult);
696     if (rc)
697     {
698         error(
699             "Failed to decode verify complete request of endpoint ID '{EID}', response code '{RC}'",
700             "EID", eid, "RC", rc);
701         rc = encode_verify_complete_resp(request->hdr.instance_id,
702                                          PLDM_ERROR_INVALID_DATA, responseMsg,
703                                          sizeof(completionCode));
704         if (rc)
705         {
706             error(
707                 "Failed to encode verify complete response of endpoint ID '{EID}', response code '{RC}'.",
708                 "EID", eid, "RC", rc);
709         }
710         return response;
711     }
712 
713     const auto& applicableComponents =
714         std::get<ApplicableComponents>(fwDeviceIDRecord);
715     const auto& comp = compImageInfos[applicableComponents[componentIndex]];
716     const auto& compVersion = std::get<7>(comp);
717     if (componentIndex < progress.size())
718     {
719         progress[componentIndex].updateState(UpdateProgress::state::Verify);
720         if (updateManager != nullptr)
721         {
722             updateManager->updateActivationProgress();
723         }
724     }
725     if (verifyResult == PLDM_FWUP_VERIFY_SUCCESS)
726     {
727         info(
728             "Component endpoint ID '{EID}' and version '{COMPONENT_VERSION}' verification complete.",
729             "EID", eid, "COMPONENT_VERSION", compVersion);
730     }
731     else
732     {
733         error(
734             "Failed to verify component endpoint ID '{EID}' and version '{COMPONENT_VERSION}' with transfer result - '{RESULT}'",
735             "EID", eid, "COMPONENT_VERSION", compVersion, "RESULT",
736             verifyResult);
737         updateManager->updateDeviceCompletion(eid, false);
738         componentUpdateStatus[componentIndex] = false;
739         sendCancelUpdateComponentRequest();
740     }
741 
742     rc = encode_verify_complete_resp(request->hdr.instance_id, completionCode,
743                                      responseMsg, sizeof(completionCode));
744     if (rc)
745     {
746         error(
747             "Failed to encode verify complete response for endpoint ID '{EID}', response code - {RC}",
748             "EID", eid, "RC", rc);
749         return response;
750     }
751 
752     return response;
753 }
754 
applyComplete(const pldm_msg * request,size_t payloadLength)755 Response DeviceUpdater::applyComplete(const pldm_msg* request,
756                                       size_t payloadLength)
757 {
758     uint8_t completionCode = PLDM_SUCCESS;
759     Response response(sizeof(pldm_msg_hdr) + sizeof(completionCode), 0);
760     auto responseMsg = new (response.data()) pldm_msg;
761 
762     uint8_t applyResult = 0;
763     bitfield16_t compActivationModification{};
764     auto rc = decode_apply_complete_req(request, payloadLength, &applyResult,
765                                         &compActivationModification);
766     if (rc)
767     {
768         error(
769             "Failed to decode apply complete request for endpoint ID '{EID}', response code '{RC}'",
770             "EID", eid, "RC", rc);
771         rc = encode_apply_complete_resp(request->hdr.instance_id,
772                                         PLDM_ERROR_INVALID_DATA, responseMsg,
773                                         sizeof(completionCode));
774         if (rc)
775         {
776             error(
777                 "Failed to encode apply complete response for endpoint ID '{EID}', response code '{RC}'",
778                 "EID", eid, "RC", rc);
779         }
780         return response;
781     }
782 
783     const auto& applicableComponents =
784         std::get<ApplicableComponents>(fwDeviceIDRecord);
785     const auto& comp = compImageInfos[applicableComponents[componentIndex]];
786     const auto& compVersion = std::get<7>(comp);
787 
788     if (applyResult == PLDM_FWUP_APPLY_SUCCESS ||
789         applyResult == PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD)
790     {
791         info(
792             "Component endpoint ID '{EID}' with '{COMPONENT_VERSION}' apply complete.",
793             "EID", eid, "COMPONENT_VERSION", compVersion);
794         if (componentIndex < progress.size())
795         {
796             progress[componentIndex].updateState(UpdateProgress::state::Apply);
797             if (updateManager != nullptr)
798             {
799                 updateManager->updateActivationProgress();
800             }
801         }
802         if (componentIndex == applicableComponents.size() - 1)
803         {
804             componentIndex = 0;
805             componentUpdateStatus.clear();
806             componentUpdateStatus[componentIndex] = true;
807             if (updateManager != nullptr)
808             {
809                 pldmRequest = std::make_unique<sdeventplus::source::Defer>(
810                     updateManager->event,
811                     std::bind(&DeviceUpdater::sendActivateFirmwareRequest,
812                               this));
813             }
814         }
815         else
816         {
817             componentIndex++;
818             componentUpdateStatus[componentIndex] = true;
819             if (updateManager != nullptr)
820             {
821                 pldmRequest = std::make_unique<sdeventplus::source::Defer>(
822                     updateManager->event,
823                     std::bind(&DeviceUpdater::sendUpdateComponentRequest, this,
824                               componentIndex));
825             }
826         }
827     }
828     else
829     {
830         error(
831             "Failed to apply component endpoint ID '{EID}' and version '{COMPONENT_VERSION}', error - {ERROR}",
832             "EID", eid, "COMPONENT_VERSION", compVersion, "ERROR", applyResult);
833         if (updateManager != nullptr)
834         {
835             updateManager->updateDeviceCompletion(eid, false);
836         }
837         componentUpdateStatus[componentIndex] = false;
838         sendCancelUpdateComponentRequest();
839     }
840 
841     rc = encode_apply_complete_resp(request->hdr.instance_id, completionCode,
842                                     responseMsg, sizeof(completionCode));
843     if (rc)
844     {
845         error(
846             "Failed to encode apply complete response for endpoint ID '{EID}', response code '{RC}'",
847             "EID", eid, "RC", rc);
848         return response;
849     }
850 
851     return response;
852 }
853 
sendActivateFirmwareRequest()854 void DeviceUpdater::sendActivateFirmwareRequest()
855 {
856     pldmRequest.reset();
857     auto instanceIdResult = updateManager->instanceIdDb.next(eid);
858     if (!instanceIdResult)
859     {
860         throw pldm::InstanceIdError(instanceIdResult.error());
861     }
862     auto instanceId = instanceIdResult.value();
863     Request request(
864         sizeof(pldm_msg_hdr) + sizeof(struct pldm_activate_firmware_req));
865     auto requestMsg = new (request.data()) pldm_msg;
866 
867     auto rc = encode_activate_firmware_req(
868         instanceId, PLDM_NOT_ACTIVATE_SELF_CONTAINED_COMPONENTS, requestMsg,
869         sizeof(pldm_activate_firmware_req));
870     if (rc)
871     {
872         updateManager->instanceIdDb.free(eid, instanceId);
873         error(
874             "Failed to encode activate firmware req for endpoint ID '{EID}', response code '{RC}'",
875             "EID", eid, "RC", rc);
876     }
877 
878     rc = updateManager->handler.registerRequest(
879         eid, instanceId, PLDM_FWUP, PLDM_ACTIVATE_FIRMWARE, std::move(request),
880         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
881             this->activateFirmware(eid, response, respMsgLen);
882         });
883     if (rc)
884     {
885         error(
886             "Failed to send activate firmware request for endpoint ID '{EID}', response code '{RC}'",
887             "EID", eid, "RC", rc);
888     }
889 }
890 
activateFirmware(mctp_eid_t eid,const pldm_msg * response,size_t respMsgLen)891 void DeviceUpdater::activateFirmware(mctp_eid_t eid, const pldm_msg* response,
892                                      size_t respMsgLen)
893 {
894     if (response == nullptr || !respMsgLen)
895     {
896         // Handle error scenario
897         error(
898             "No response received for activate firmware for endpoint ID '{EID}'",
899             "EID", eid);
900         updateManager->updateDeviceCompletion(eid, false);
901         return;
902     }
903 
904     uint8_t completionCode = 0;
905     uint16_t estimatedTimeForActivation = 0;
906 
907     auto rc = decode_activate_firmware_resp(
908         response, respMsgLen, &completionCode, &estimatedTimeForActivation);
909     if (rc)
910     {
911         // Handle error scenario
912         error(
913             "Failed to decode activate firmware response for endpoint ID '{EID}', response code '{RC}'",
914             "EID", eid, "RC", rc);
915         return;
916     }
917     if (completionCode)
918     {
919         // Handle error scenario
920         error(
921             "Failed to activate firmware response for endpoint ID '{EID}', completion code '{CC}'",
922             "EID", eid, "CC", completionCode);
923         updateManager->updateDeviceCompletion(eid, false);
924         return;
925     }
926 
927     activationComplete = true;
928     if (updateManager == nullptr)
929     {
930         return;
931     }
932 
933     updateManager->updateActivationProgress();
934     updateManager->updateDeviceCompletion(eid, true);
935 }
936 
sendCancelUpdateComponentRequest()937 void DeviceUpdater::sendCancelUpdateComponentRequest()
938 {
939     pldmRequest.reset();
940     auto instanceIdResult = updateManager->instanceIdDb.next(eid);
941     if (!instanceIdResult)
942     {
943         throw pldm::InstanceIdError(instanceIdResult.error());
944     }
945     auto instanceId = instanceIdResult.value();
946     Request request(sizeof(pldm_msg_hdr));
947     auto requestMsg = new (request.data()) pldm_msg;
948 
949     auto rc = encode_cancel_update_component_req(
950         instanceId, requestMsg, PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES);
951     if (rc)
952     {
953         updateManager->instanceIdDb.free(eid, instanceId);
954         error(
955             "Failed to encode cancel update component request for endpoint ID '{EID}', component index '{COMPONENT_INDEX}', response code '{RC}'",
956             "EID", eid, "COMPONENT_INDEX", componentIndex, "RC", rc);
957         return;
958     }
959 
960     rc = updateManager->handler.registerRequest(
961         eid, instanceId, PLDM_FWUP, PLDM_CANCEL_UPDATE_COMPONENT,
962         std::move(request),
963         [this](mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen) {
964             this->cancelUpdateComponent(eid, response, respMsgLen);
965         });
966     if (rc)
967     {
968         error(
969             "Failed to send cancel update component request for endpoint ID '{EID}', component index '{COMPONENT_INDEX}', response code '{RC}'",
970             "EID", eid, "COMPONENT_INDEX", componentIndex, "RC", rc);
971     }
972 }
973 
cancelUpdateComponent(mctp_eid_t eid,const pldm_msg * response,size_t respMsgLen)974 void DeviceUpdater::cancelUpdateComponent(
975     mctp_eid_t eid, const pldm_msg* response, size_t respMsgLen)
976 {
977     // Check if response is valid
978     if (response == nullptr || !respMsgLen)
979     {
980         error(
981             "No response received for cancel update component for endpoint ID '{EID}'",
982             "EID", eid);
983         return;
984     }
985 
986     uint8_t completionCode = 0;
987     auto rc = decode_cancel_update_component_resp(response, respMsgLen,
988                                                   &completionCode);
989     if (rc)
990     {
991         error(
992             "Failed to decode cancel update component response for endpoint ID '{EID}', component index '{COMPONENT_INDEX}', completion code '{CC}'",
993             "EID", eid, "COMPONENT_INDEX", componentIndex, "CC",
994             completionCode);
995         return;
996     }
997     if (completionCode)
998     {
999         error(
1000             "Failed to cancel update component for endpoint ID '{EID}', component index '{COMPONENT_INDEX}', completion code '{CC}'",
1001             "EID", eid, "COMPONENT_INDEX", componentIndex, "CC",
1002             completionCode);
1003         return;
1004     }
1005 
1006     const auto& applicableComponents =
1007         std::get<ApplicableComponents>(fwDeviceIDRecord);
1008     // Check if this is the last component being cancelled
1009     if (componentIndex == applicableComponents.size() - 1)
1010     {
1011         for (auto& compStatus : componentUpdateStatus)
1012         {
1013             if (compStatus.second)
1014             {
1015                 // If at least one component update succeeded, proceed with
1016                 // activation
1017                 componentIndex = 0;
1018                 componentUpdateStatus.clear();
1019                 pldmRequest = std::make_unique<sdeventplus::source::Defer>(
1020                     updateManager->event,
1021                     std::bind(&DeviceUpdater::sendActivateFirmwareRequest,
1022                               this));
1023                 return;
1024             }
1025         }
1026         updateManager->updateDeviceCompletion(eid, false);
1027     }
1028     else
1029     {
1030         // Move to next component and update its status
1031         componentIndex++;
1032         componentUpdateStatus[componentIndex] = true;
1033         pldmRequest = std::make_unique<sdeventplus::source::Defer>(
1034             updateManager->event,
1035             std::bind(&DeviceUpdater::sendUpdateComponentRequest, this,
1036                       componentIndex));
1037     }
1038     return;
1039 }
1040 
getProgress() const1041 uint8_t DeviceUpdater::getProgress() const
1042 {
1043     if (progress.empty())
1044     {
1045         return 0;
1046     }
1047 
1048     if (activationComplete)
1049     {
1050         return firmwareActivationProgressPercent;
1051     }
1052 
1053     uint64_t totalSize = 0;
1054     uint64_t weightedProgress = 0;
1055     for (const auto& el : progress)
1056     {
1057         totalSize += el.getTotalSize();
1058         weightedProgress +=
1059             el.getTotalSize() * static_cast<uint64_t>(el.getProgress());
1060     }
1061 
1062     if (totalSize == 0)
1063     {
1064         error("total component size is 0 on eid {EID}", "EID", eid);
1065         return 0;
1066     }
1067 
1068     const double percentage =
1069         static_cast<double>(weightedProgress) / static_cast<double>(totalSize);
1070 
1071     return static_cast<uint8_t>(std::floor(percentage));
1072 }
1073 
1074 } // namespace fw_update
1075 
1076 } // namespace pldm
1077