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