blob: 116657c92b9fc85a49753cc51e0f54ac3b6b7168 [file] [log] [blame]
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2#include <stdint.h>
3#include <stdbool.h>
4#include <string.h>
5#include <stdlib.h>
6
7#include <libpldm/pldm.h>
8#include <libpldm/firmware_update.h>
9#include <libpldm/firmware_fd.h>
10#include <libpldm/utils.h>
11#include <compiler.h>
12#include <msgbuf.h>
13
14#include "fd-internal.h"
15
16/* FD_T1 Update mode idle timeout, 120 seconds (range [60s, 120s])*/
17static const pldm_fd_time_t DEFAULT_FD_T1_TIMEOUT = 120000;
18
19/* FD_T2 "Retry request for firmware data", 1 second (range [1s, 5s]) */
20static const pldm_fd_time_t DEFAULT_FD_T2_RETRY_TIME = 1000;
21
22static const uint8_t INSTANCE_ID_COUNT = 32;
23static const uint8_t PROGRESS_PERCENT_NOT_SUPPORTED = 101;
24
25/* Ensure that public definition is kept updated */
26static_assert(alignof(struct pldm_fd) == PLDM_ALIGNOF_PLDM_FD,
27 "PLDM_ALIGNOF_PLDM_FD wrong");
28
29/* Maybe called with success or failure completion codes, though
30 * success only makes sense for responses without a body.
31 * Returns 0 or negative errno. */
32LIBPLDM_CC_NONNULL
33static int pldm_fd_reply_cc(uint8_t ccode,
34 const struct pldm_header_info *req_hdr,
35 struct pldm_msg *resp, size_t *resp_payload_len)
36{
37 int status;
38
39 /* 1 byte completion code */
40 if (*resp_payload_len < 1) {
41 return -EOVERFLOW;
42 }
43 *resp_payload_len = 1;
44
45 status = encode_cc_only_resp(req_hdr->instance, PLDM_FWUP,
46 req_hdr->command, ccode, resp);
47 if (status != PLDM_SUCCESS) {
48 return -EINVAL;
49 }
50 return 0;
51}
52
53/* Must be called with a negative errno.
54 * Returns 0 or negative errno. */
55LIBPLDM_CC_NONNULL
56static int pldm_fd_reply_errno(int err, const struct pldm_header_info *req_hdr,
57 struct pldm_msg *resp, size_t *resp_payload_len)
58{
59 uint8_t ccode = PLDM_ERROR;
60
61 assert(err < 0);
62 switch (err) {
63 case -EINVAL:
64 // internal error, shouldn't occur.
65 ccode = PLDM_ERROR;
66 break;
67 case -EPROTO:
68 // Bad data from peer
69 ccode = PLDM_ERROR_INVALID_DATA;
70 break;
71 case -EOVERFLOW:
72 case -EBADMSG:
73 // Bad data from peer
74 ccode = PLDM_ERROR_INVALID_LENGTH;
75 break;
76 default:
77 // general error
78 ccode = PLDM_ERROR;
79 }
80
81 return pldm_fd_reply_cc(ccode, req_hdr, resp, resp_payload_len);
82}
83
84LIBPLDM_CC_NONNULL
85static void pldm_fd_set_state(struct pldm_fd *fd,
86 enum pldm_firmware_device_states state)
87{
88 /* pldm_fd_set_idle should be used instead */
89 assert(state != PLDM_FD_STATE_IDLE);
90
91 if (fd->state == state) {
92 return;
93 }
94
95 fd->prev_state = fd->state;
96 fd->state = state;
97}
98
99LIBPLDM_CC_NONNULL
100static void pldm_fd_set_idle(struct pldm_fd *fd,
101 enum pldm_get_status_reason_code_values reason)
102{
103 fd->prev_state = fd->state;
104 fd->state = PLDM_FD_STATE_IDLE;
105 fd->reason = reason;
106 fd->ua_address_set = false;
107}
108
109LIBPLDM_CC_NONNULL
110static void pldm_fd_idle_timeout(struct pldm_fd *fd)
111{
112 enum pldm_get_status_reason_code_values reason = PLDM_FD_INITIALIZATION;
113
114 switch (fd->state) {
115 case PLDM_FD_STATE_IDLE:
116 return;
117 case PLDM_FD_STATE_LEARN_COMPONENTS:
118 reason = PLDM_FD_TIMEOUT_LEARN_COMPONENT;
119 break;
120 case PLDM_FD_STATE_READY_XFER:
121 reason = PLDM_FD_TIMEOUT_READY_XFER;
122 break;
123 case PLDM_FD_STATE_DOWNLOAD:
124 reason = PLDM_FD_TIMEOUT_DOWNLOAD;
125 break;
126 case PLDM_FD_STATE_VERIFY:
127 reason = PLDM_FD_TIMEOUT_VERIFY;
128 break;
129 case PLDM_FD_STATE_APPLY:
130 reason = PLDM_FD_TIMEOUT_APPLY;
131 break;
132 case PLDM_FD_STATE_ACTIVATE:
133 /* Not a timeout */
134 reason = PLDM_FD_ACTIVATE_FW;
135 break;
136 }
137
138 pldm_fd_set_idle(fd, reason);
139}
140
141LIBPLDM_CC_NONNULL
142static void pldm_fd_get_aux_state(const struct pldm_fd *fd, uint8_t *aux_state,
143 uint8_t *aux_state_status)
144{
145 *aux_state_status = 0;
146
147 switch (fd->req.state) {
148 case PLDM_FD_REQ_UNUSED:
149 *aux_state = PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER;
150 break;
151 case PLDM_FD_REQ_SENT:
152 *aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
153 break;
154 case PLDM_FD_REQ_READY:
155 if (fd->req.complete) {
156 *aux_state = PLDM_FD_OPERATION_SUCCESSFUL;
157 } else {
158 *aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
159 }
160 break;
161 case PLDM_FD_REQ_FAILED:
162 *aux_state = PLDM_FD_OPERATION_FAILED;
163 *aux_state_status = fd->req.result;
164 break;
165 }
166}
167
168LIBPLDM_CC_NONNULL
169static uint64_t pldm_fd_now(struct pldm_fd *fd)
170{
171 return fd->ops->now(fd->ops_ctx);
172}
173
174LIBPLDM_CC_NONNULL
175static bool pldm_fd_req_should_send(struct pldm_fd *fd)
176{
177 pldm_fd_time_t now = pldm_fd_now(fd);
178
179 switch (fd->req.state) {
180 case PLDM_FD_REQ_UNUSED:
181 assert(false);
182 return false;
183 case PLDM_FD_REQ_READY:
184 return true;
185 case PLDM_FD_REQ_FAILED:
186 return false;
187 case PLDM_FD_REQ_SENT:
188 if (now < fd->req.sent_time) {
189 /* Time went backwards */
190 return false;
191 }
192
193 /* Send if retry time has elapsed */
194 return (now - fd->req.sent_time) >= fd->fd_t2_retry_time;
195 }
196 return false;
197}
198
199/* Allocate the next instance ID. Only one request is outstanding so cycling
200 * through the range is OK */
201LIBPLDM_CC_NONNULL
202static uint8_t pldm_fd_req_next_instance(struct pldm_fd_req *req)
203{
204 req->instance_id = (req->instance_id + 1) % INSTANCE_ID_COUNT;
205 return req->instance_id;
206}
207
208LIBPLDM_CC_NONNULL
209static int pldm_fd_qdi(struct pldm_fd *fd, const struct pldm_header_info *hdr,
210 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
211 size_t req_payload_len, struct pldm_msg *resp,
212 size_t *resp_payload_len)
213{
214 uint8_t descriptor_count;
215 const struct pldm_descriptor *descriptors;
216 int rc;
217
218 /* QDI has no request data */
219 if (req_payload_len != PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES) {
220 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
221 resp_payload_len);
222 }
223
224 /* Retrieve platform-specific data */
225 rc = fd->ops->device_identifiers(fd->ops_ctx, &descriptor_count,
226 &descriptors);
227 if (rc) {
228 return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
229 resp_payload_len);
230 }
231
232 rc = encode_query_device_identifiers_resp(hdr->instance,
233 descriptor_count, descriptors,
234 resp, resp_payload_len);
235
236 if (rc) {
237 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
238 }
239
240 return 0;
241}
242
243LIBPLDM_CC_NONNULL
244static int pldm_fd_fw_param(struct pldm_fd *fd,
245 const struct pldm_header_info *hdr,
246 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
247 size_t req_payload_len, struct pldm_msg *resp,
248 size_t *resp_payload_len)
249{
250 uint16_t entry_count;
251 const struct pldm_firmware_component_standalone **entries;
252 struct pldm_msgbuf _buf;
253 struct pldm_msgbuf *buf = &_buf;
254 int rc;
255
256 /* No request data */
257 if (req_payload_len != PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES) {
258 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
259 resp_payload_len);
260 }
261
262 /* Retrieve platform-specific data */
263 rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
264 if (rc) {
265 return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
266 resp_payload_len);
267 }
268
269 rc = pldm_msgbuf_init_errno(buf, 0, resp->payload, *resp_payload_len);
270 if (rc) {
271 return rc;
272 }
273
274 /* Add the fixed parameters */
275 {
276 struct pldm_get_firmware_parameters_resp_full fwp = {
277 .completion_code = PLDM_SUCCESS,
278 // TODO defaulted to 0, could have a callback.
279 .capabilities_during_update = { 0 },
280 .comp_count = entry_count,
281 };
282 /* fill active and pending strings */
283 rc = fd->ops->imageset_versions(
284 fd->ops_ctx, &fwp.active_comp_image_set_ver_str,
285 &fwp.pending_comp_image_set_ver_str);
286 if (rc) {
287 return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
288 resp_payload_len);
289 }
290
291 size_t len = buf->remaining;
292 rc = encode_get_firmware_parameters_resp(hdr->instance, &fwp,
293 resp, &len);
294 if (rc) {
295 return pldm_fd_reply_errno(rc, hdr, resp,
296 resp_payload_len);
297 }
298 rc = pldm_msgbuf_skip(buf, len);
299 if (rc) {
300 return rc;
301 }
302 }
303
304 /* Add the component table entries */
305 for (uint16_t i = 0; i < entry_count; i++) {
306 const struct pldm_firmware_component_standalone *e = entries[i];
307 void *out = NULL;
308 size_t len;
309
310 struct pldm_component_parameter_entry_full comp = {
311 .comp_classification = e->comp_classification,
312 .comp_identifier = e->comp_identifier,
313 .comp_classification_index =
314 e->comp_classification_index,
315
316 .active_ver = e->active_ver,
317 .pending_ver = e->pending_ver,
318
319 .comp_activation_methods = e->comp_activation_methods,
320 .capabilities_during_update =
321 e->capabilities_during_update,
322 };
323
324 if (pldm_msgbuf_peek_remaining(buf, &out, &len)) {
325 return rc;
326 }
327 rc = encode_get_firmware_parameters_resp_comp_entry(&comp, out,
328 &len);
329 if (rc) {
330 return pldm_fd_reply_errno(rc, hdr, resp,
331 resp_payload_len);
332 }
333 rc = pldm_msgbuf_skip(buf, len);
334 if (rc) {
335 return rc;
336 }
337 }
338
339 return pldm_msgbuf_destroy_used(buf, *resp_payload_len,
340 resp_payload_len);
341}
342
343LIBPLDM_CC_NONNULL
344static int pldm_fd_request_update(struct pldm_fd *fd,
345 const struct pldm_header_info *hdr,
346 const struct pldm_msg *req,
347 size_t req_payload_len, struct pldm_msg *resp,
348 size_t *resp_payload_len, uint8_t address)
349{
350 struct pldm_request_update_req_full upd;
351 const struct pldm_request_update_resp resp_data = {
352 .fd_meta_data_len = 0,
353 .fd_will_send_pkg_data = 0,
354 };
355 int rc;
356
357 if (fd->state != PLDM_FD_STATE_IDLE) {
358 return pldm_fd_reply_cc(PLDM_FWUP_ALREADY_IN_UPDATE_MODE, hdr,
359 resp, resp_payload_len);
360 }
361
362 rc = decode_request_update_req(req, req_payload_len, &upd);
363 if (rc) {
364 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
365 }
366
367 /* No metadata nor pkg data */
368 rc = encode_request_update_resp(hdr->instance, &resp_data, resp,
369 resp_payload_len);
370 if (rc) {
371 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
372 }
373
374 // TODO pass num_of_comp and image_set_ver to application?
375
376 fd->max_transfer =
377 fd->ops->transfer_size(fd->ops_ctx, upd.max_transfer_size);
378 if (fd->max_transfer > upd.max_transfer_size) {
379 /* Limit to UA's size */
380 fd->max_transfer = upd.max_transfer_size;
381 }
382 if (fd->max_transfer < PLDM_FWUP_BASELINE_TRANSFER_SIZE) {
383 /* Don't let it be zero, that will loop forever */
384 fd->max_transfer = PLDM_FWUP_BASELINE_TRANSFER_SIZE;
385 }
386 fd->ua_address = address;
387 fd->ua_address_set = true;
388
389 pldm_fd_set_state(fd, PLDM_FD_STATE_LEARN_COMPONENTS);
390
391 return 0;
392}
393
394/* wrapper around ops->cancel, will only run ops->cancel when a component update is
395 * active */
396LIBPLDM_CC_NONNULL
397static void pldm_fd_maybe_cancel_component(struct pldm_fd *fd)
398{
399 bool cancel = false;
400
401 switch (fd->state) {
402 case PLDM_FD_STATE_DOWNLOAD:
403 case PLDM_FD_STATE_VERIFY:
404 cancel = true;
405 break;
406 case PLDM_FD_STATE_APPLY:
407 /* In apply state, once the application ops->apply() has completed
408 * successfully the component is no longer in update state.
409 * In that case the cancel should not be forwarded to the application.
410 * This can occur if a cancel is received while waiting for the
411 * response to a success ApplyComplete. */
412 cancel = !(fd->req.complete &&
413 fd->req.result == PLDM_FWUP_APPLY_SUCCESS);
414 break;
415 default:
416 break;
417 }
418
419 if (cancel) {
420 /* Call the platform handler for the current component in progress */
421 fd->ops->cancel_update_component(fd->ops_ctx, &fd->update_comp);
422 }
423}
424
425/* Wrapper around ops->update_component() that first checks that the component
426 * is in the list returned from ops->components() */
427LIBPLDM_CC_NONNULL
428static enum pldm_component_response_codes pldm_fd_check_update_component(
429 struct pldm_fd *fd, bool update,
430 const struct pldm_firmware_update_component *comp)
431{
432 bool found;
433 uint16_t entry_count;
434 int rc;
435
436 const struct pldm_firmware_component_standalone **entries;
437 rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
438 if (rc) {
439 return PLDM_CRC_COMP_NOT_SUPPORTED;
440 }
441
442 found = false;
443 for (uint16_t i = 0; i < entry_count; i++) {
444 if (entries[i]->comp_classification ==
445 comp->comp_classification &&
446 entries[i]->comp_identifier == comp->comp_identifier &&
447 entries[i]->comp_classification_index ==
448 comp->comp_classification_index) {
449 found = true;
450 break;
451 }
452 }
453 if (found) {
454 return fd->ops->update_component(fd->ops_ctx, update, comp);
455 }
456 return PLDM_CRC_COMP_NOT_SUPPORTED;
457}
458
459LIBPLDM_CC_NONNULL
460static int pldm_fd_pass_comp(struct pldm_fd *fd,
461 const struct pldm_header_info *hdr,
462 const struct pldm_msg *req, size_t req_payload_len,
463 struct pldm_msg *resp, size_t *resp_payload_len)
464{
465 struct pldm_pass_component_table_req_full pcomp;
466 uint8_t comp_response_code;
467 int rc;
468
469 if (fd->state != PLDM_FD_STATE_LEARN_COMPONENTS) {
470 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
471 hdr, resp, resp_payload_len);
472 }
473
474 /* fd->update_comp is used as temporary storage during PassComponent validation */
475 /* Some portions are unused for PassComponentTable */
476 fd->update_comp.comp_image_size = 0;
477 fd->update_comp.update_option_flags.value = 0;
478
479 rc = decode_pass_component_table_req(req, req_payload_len, &pcomp);
480 if (rc) {
481 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
482 }
483
484 fd->update_comp.comp_classification = pcomp.comp_classification;
485 fd->update_comp.comp_identifier = pcomp.comp_identifier;
486 fd->update_comp.comp_classification_index =
487 pcomp.comp_classification_index;
488 fd->update_comp.comp_comparison_stamp = pcomp.comp_comparison_stamp;
489 memcpy(&fd->update_comp.version, &pcomp.version, sizeof(pcomp.version));
490
491 comp_response_code =
492 pldm_fd_check_update_component(fd, false, &fd->update_comp);
493
494 const struct pldm_pass_component_table_resp resp_data = {
495 /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
496 .comp_resp = (comp_response_code != 0),
497 .comp_resp_code = comp_response_code,
498 };
499
500 rc = encode_pass_component_table_resp(hdr->instance, &resp_data, resp,
501 resp_payload_len);
502 if (rc) {
503 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
504 }
505
506 if (pcomp.transfer_flag & PLDM_END) {
507 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
508 }
509
510 return 0;
511}
512
513LIBPLDM_CC_NONNULL
514static int pldm_fd_update_comp(struct pldm_fd *fd,
515 const struct pldm_header_info *hdr,
516 const struct pldm_msg *req,
517 size_t req_payload_len, struct pldm_msg *resp,
518 size_t *resp_payload_len)
519{
520 struct pldm_update_component_req_full up;
521 uint8_t comp_response_code;
522 int rc;
523
524 if (fd->state != PLDM_FD_STATE_READY_XFER) {
525 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
526 hdr, resp, resp_payload_len);
527 }
528
529 rc = decode_update_component_req(req, req_payload_len, &up);
530 if (rc) {
531 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
532 }
533
534 /* Store update_comp to pass to further callbacks. This persists
535 * until the component update completes or is cancelled */
536 fd->update_comp.comp_classification = up.comp_classification;
537 fd->update_comp.comp_identifier = up.comp_identifier;
538 fd->update_comp.comp_classification_index =
539 up.comp_classification_index;
540 fd->update_comp.comp_comparison_stamp = up.comp_comparison_stamp;
541 fd->update_comp.comp_image_size = up.comp_image_size;
542 fd->update_comp.update_option_flags = up.update_option_flags;
543 memcpy(&fd->update_comp.version, &up.version, sizeof(up.version));
544
545 comp_response_code =
546 pldm_fd_check_update_component(fd, true, &fd->update_comp);
547
548 // Mask to only the "Force Update" flag, others are not handled.
549 bitfield32_t update_flags = {
550 .bits.bit0 = fd->update_comp.update_option_flags.bits.bit0
551 };
552
553 const struct pldm_update_component_resp resp_data = {
554 /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
555 .comp_compatibility_resp = (comp_response_code != 0),
556 .comp_compatibility_resp_code = comp_response_code,
557 .update_option_flags_enabled = update_flags,
558 .time_before_req_fw_data = 0,
559 };
560
561 rc = encode_update_component_resp(hdr->instance, &resp_data, resp,
562 resp_payload_len);
563 if (rc) {
564 /* Encoding response failed */
565 if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
566 /* Inform the application of cancellation. Call it directly
567 * rather than going through pldm_fd_maybe_cancel_component() */
568 fd->ops->cancel_update_component(fd->ops_ctx,
569 &fd->update_comp);
570 }
571 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
572 }
573
574 /* Set up download state */
575 if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
576 memset(&fd->specific, 0x0, sizeof(fd->specific));
577 fd->update_flags = update_flags;
578 fd->req.state = PLDM_FD_REQ_READY;
579 fd->req.complete = false;
580 pldm_fd_set_state(fd, PLDM_FD_STATE_DOWNLOAD);
581 }
582
583 return 0;
584}
585
586LIBPLDM_CC_NONNULL
587static int pldm_fd_get_status(struct pldm_fd *fd,
588 const struct pldm_header_info *hdr,
589 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
590 size_t req_payload_len, struct pldm_msg *resp,
591 size_t *resp_payload_len)
592{
593 int rc;
594
595 /* No request data */
596 if (req_payload_len != PLDM_GET_STATUS_REQ_BYTES) {
597 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
598 resp_payload_len);
599 }
600
601 /* Defaults */
602 struct pldm_get_status_resp st = {
603 .current_state = fd->state,
604 .previous_state = fd->prev_state,
605 .progress_percent = PROGRESS_PERCENT_NOT_SUPPORTED,
606 };
607
608 pldm_fd_get_aux_state(fd, &st.aux_state, &st.aux_state_status);
609
610 switch (fd->state) {
611 case PLDM_FD_STATE_IDLE:
612 st.reason_code = fd->reason;
613 break;
614 case PLDM_FD_STATE_DOWNLOAD:
615 if (fd->update_comp.comp_image_size > 0) {
616 uint32_t one_percent =
617 fd->update_comp.comp_image_size / 100;
618 if (fd->update_comp.comp_image_size % 100 != 0) {
619 one_percent += 1;
620 }
621 st.progress_percent =
622 (fd->specific.download.offset / one_percent);
623 }
624 st.update_option_flags_enabled = fd->update_flags;
625 break;
626 case PLDM_FD_STATE_VERIFY:
627 st.update_option_flags_enabled = fd->update_flags;
628 st.progress_percent = fd->specific.verify.progress_percent;
629 break;
630 case PLDM_FD_STATE_APPLY:
631 st.update_option_flags_enabled = fd->update_flags;
632 st.progress_percent = fd->specific.apply.progress_percent;
633 break;
634 default:
635 break;
636 }
637
638 rc = encode_get_status_resp(hdr->instance, &st, resp, resp_payload_len);
639 if (rc) {
640 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
641 }
642
643 return 0;
644}
645
646LIBPLDM_CC_NONNULL
647static int pldm_fd_cancel_update_comp(
648 struct pldm_fd *fd, const struct pldm_header_info *hdr,
649 const struct pldm_msg *req LIBPLDM_CC_UNUSED, size_t req_payload_len,
650 struct pldm_msg *resp, size_t *resp_payload_len)
651{
652 int rc;
653
654 /* No request data */
655 if (req_payload_len != PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES) {
656 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
657 resp_payload_len);
658 }
659
660 switch (fd->state) {
661 case PLDM_FD_STATE_DOWNLOAD:
662 case PLDM_FD_STATE_VERIFY:
663 case PLDM_FD_STATE_APPLY:
664 break;
665 default:
666 return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
667 resp_payload_len);
668 }
669
670 /* No response payload */
671 rc = pldm_fd_reply_cc(PLDM_SUCCESS, hdr, resp, resp_payload_len);
672 if (rc) {
673 return rc;
674 }
675
676 pldm_fd_maybe_cancel_component(fd);
677 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
678
679 return 0;
680}
681
682LIBPLDM_CC_NONNULL
683static int pldm_fd_cancel_update(struct pldm_fd *fd,
684 const struct pldm_header_info *hdr,
685 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
686 size_t req_payload_len, struct pldm_msg *resp,
687 size_t *resp_payload_len)
688{
689 int rc;
690
691 /* No request data */
692 if (req_payload_len != PLDM_CANCEL_UPDATE_REQ_BYTES) {
693 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
694 resp_payload_len);
695 }
696
697 if (fd->state == PLDM_FD_STATE_IDLE) {
698 return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
699 resp_payload_len);
700 }
701
702 /* Assume non_functioning_component_indication = False, in future
703 * could add a platform callback */
704 const struct pldm_cancel_update_resp resp_data = {
705 .non_functioning_component_indication = 0,
706 .non_functioning_component_bitmap = 0,
707 };
708 rc = encode_cancel_update_resp(hdr->instance, &resp_data, resp,
709 resp_payload_len);
710 if (rc) {
711 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
712 }
713
714 pldm_fd_maybe_cancel_component(fd);
715 pldm_fd_set_idle(fd, PLDM_FD_CANCEL_UPDATE);
716
717 return 0;
718}
719
720LIBPLDM_CC_NONNULL
721static int pldm_fd_activate_firmware(struct pldm_fd *fd,
722 const struct pldm_header_info *hdr,
723 const struct pldm_msg *req,
724 size_t req_payload_len,
725 struct pldm_msg *resp,
726 size_t *resp_payload_len)
727{
728 uint16_t estimated_time;
729 uint8_t ccode;
730 int rc;
731 bool self_contained;
732
733 rc = decode_activate_firmware_req(req, req_payload_len,
734 &self_contained);
735 if (rc) {
736 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
737 }
738
739 if (fd->state != PLDM_FD_STATE_READY_XFER) {
740 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
741 hdr, resp, resp_payload_len);
742 }
743
744 estimated_time = 0;
745 ccode = fd->ops->activate(fd->ops_ctx, self_contained, &estimated_time);
746
747 if (ccode == PLDM_SUCCESS ||
748 ccode == PLDM_FWUP_ACTIVATION_NOT_REQUIRED) {
749 /* Transition through states so that the prev_state is correct */
750 pldm_fd_set_state(fd, PLDM_FD_STATE_ACTIVATE);
751 pldm_fd_set_idle(fd, PLDM_FD_ACTIVATE_FW);
752 }
753
754 if (ccode == PLDM_SUCCESS) {
755 const struct pldm_activate_firmware_resp resp_data = {
756 .estimated_time_activation = estimated_time,
757 };
758 rc = encode_activate_firmware_resp(hdr->instance, &resp_data,
759 resp, resp_payload_len);
760 if (rc) {
761 return pldm_fd_reply_errno(rc, hdr, resp,
762 resp_payload_len);
763 }
764 } else {
765 return pldm_fd_reply_cc(ccode, hdr, resp, resp_payload_len);
766 }
767
768 return 0;
769}
770
771LIBPLDM_CC_NONNULL
772static uint32_t pldm_fd_fwdata_size(struct pldm_fd *fd)
773{
774 uint32_t size;
775
776 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
777 assert(false);
778 return 0;
779 }
780
781 if (fd->specific.download.offset > fd->update_comp.comp_image_size) {
782 assert(false);
783 return 0;
784 }
785 size = fd->update_comp.comp_image_size - fd->specific.download.offset;
786
787 if (size > fd->max_transfer) {
788 size = fd->max_transfer;
789 }
790 return size;
791}
792
793LIBPLDM_CC_NONNULL
794static int pldm_fd_handle_fwdata_resp(struct pldm_fd *fd,
795 const struct pldm_msg *resp,
796 size_t resp_payload_len)
797{
798 struct pldm_fd_download *dl;
799 uint32_t fwdata_size;
800 uint8_t res;
801
802 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
803 return -EPROTO;
804 }
805
806 if (fd->req.state != PLDM_FD_REQ_SENT) {
807 /* Not waiting for a response, ignore it */
808 return -EPROTO;
809 }
810
811 dl = &fd->specific.download;
812 if (fd->req.complete) {
813 /* Received data after completion */
814 return -EPROTO;
815 }
816
817 switch (resp->payload[0]) {
818 case PLDM_SUCCESS:
819 break;
820 case PLDM_FWUP_RETRY_REQUEST_FW_DATA:
821 /* Just return, let the retry timer send another request later */
822 return 0;
823 default:
824 /* Send a TransferComplete failure */
825 fd->req.state = PLDM_FD_REQ_READY;
826 fd->req.complete = true;
827 fd->req.result = PLDM_FWUP_FD_ABORTED_TRANSFER;
828 return 0;
829 }
830
831 /* Handle the received data */
832
833 fwdata_size = pldm_fd_fwdata_size(fd);
834 if (resp_payload_len != fwdata_size + 1) {
835 /* Data is incorrect size. Could indicate MCTP corruption, drop it
836 * and let retry timer handle it */
837 return -EOVERFLOW;
838 }
839
840 /* Check pldm_fd_fwdata_size calculation, should not fail */
841 if (dl->offset + fwdata_size < dl->offset ||
842 dl->offset + fwdata_size > fd->update_comp.comp_image_size) {
843 assert(false);
844 return -EINVAL;
845 }
846
847 /* Provide the data chunk to the device */
848 res = fd->ops->firmware_data(fd->ops_ctx, dl->offset, &resp->payload[1],
849 fwdata_size, &fd->update_comp);
850
851 fd->req.state = PLDM_FD_REQ_READY;
852 if (res == PLDM_FWUP_TRANSFER_SUCCESS) {
853 /* Move to next offset */
854 dl->offset += fwdata_size;
855 if (dl->offset == fd->update_comp.comp_image_size) {
856 /* Mark as complete, next progress() call will send the TransferComplete request */
857 fd->req.complete = true;
858 fd->req.result = PLDM_FWUP_TRANSFER_SUCCESS;
859 }
860 } else {
861 /* Pass the callback error as the TransferResult */
862 fd->req.complete = true;
863 fd->req.result = res;
864 }
865
866 return 0;
867}
868
869LIBPLDM_CC_NONNULL
870static int pldm_fd_handle_transfer_complete_resp(
871 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
872 size_t resp_payload_len LIBPLDM_CC_UNUSED)
873{
874 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
875 return -EPROTO;
876 }
877
878 if (fd->req.state != PLDM_FD_REQ_SENT) {
879 /* Not waiting for a response, ignore it */
880 return -EPROTO;
881 }
882
883 if (!fd->req.complete) {
884 /* Were waiting for RequestFirmwareData instead, ignore it */
885 return -EPROTO;
886 }
887
888 /* Disregard the response completion code */
889
890 /* Next state depends whether the transfer succeeded */
891 if (fd->req.result == PLDM_FWUP_TRANSFER_SUCCESS) {
892 /* Switch to Verify */
893 memset(&fd->specific, 0x0, sizeof(fd->specific));
894 fd->specific.verify.progress_percent =
895 PROGRESS_PERCENT_NOT_SUPPORTED;
896 fd->req.state = PLDM_FD_REQ_READY;
897 fd->req.complete = false;
898 pldm_fd_set_state(fd, PLDM_FD_STATE_VERIFY);
899 } else {
900 /* Wait for UA to cancel */
901 fd->req.state = PLDM_FD_REQ_FAILED;
902 }
903 return 0;
904}
905
906LIBPLDM_CC_NONNULL
907static int pldm_fd_handle_verify_complete_resp(
908 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
909 size_t resp_payload_len LIBPLDM_CC_UNUSED)
910{
911 if (fd->state != PLDM_FD_STATE_VERIFY) {
912 return -EPROTO;
913 }
914
915 if (fd->req.state != PLDM_FD_REQ_SENT) {
916 /* Not waiting for a response, ignore it */
917 return -EPROTO;
918 }
919
920 assert(fd->req.complete);
921
922 /* Disregard the response completion code */
923
924 /* Next state depends whether the verify succeeded */
925 if (fd->req.result == PLDM_FWUP_VERIFY_SUCCESS) {
926 /* Switch to Apply */
927 memset(&fd->specific, 0x0, sizeof(fd->specific));
928 fd->specific.apply.progress_percent =
929 PROGRESS_PERCENT_NOT_SUPPORTED;
930 fd->req.state = PLDM_FD_REQ_READY;
931 fd->req.complete = false;
932 pldm_fd_set_state(fd, PLDM_FD_STATE_APPLY);
933 } else {
934 /* Wait for UA to cancel */
935 fd->req.state = PLDM_FD_REQ_FAILED;
936 }
937 return 0;
938}
939
940LIBPLDM_CC_NONNULL
941static int pldm_fd_handle_apply_complete_resp(
942 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
943 size_t resp_payload_len LIBPLDM_CC_UNUSED)
944{
945 if (fd->state != PLDM_FD_STATE_APPLY) {
946 return -EPROTO;
947 }
948
949 if (fd->req.state != PLDM_FD_REQ_SENT) {
950 /* Not waiting for a response, ignore it */
951 return -EPROTO;
952 }
953
954 assert(fd->req.complete);
955
956 /* Disregard the response completion code */
957
958 /* Next state depends whether the apply succeeded */
959 if (fd->req.result == PLDM_FWUP_APPLY_SUCCESS) {
960 /* Switch to ReadyXfer */
961 fd->req.state = PLDM_FD_REQ_UNUSED;
962 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
963 } else {
964 /* Wait for UA to cancel */
965 fd->req.state = PLDM_FD_REQ_FAILED;
966 }
967 return 0;
968}
969
970LIBPLDM_CC_NONNULL
971static int pldm_fd_handle_resp(struct pldm_fd *fd, pldm_tid_t address,
972 const void *resp_msg, size_t resp_len)
973{
974 size_t resp_payload_len;
975 const struct pldm_msg *resp = resp_msg;
976
977 if (!(fd->ua_address_set && fd->ua_address == address)) {
978 // Either an early response, or a response from a wrong TID */
979 return -EBUSY;
980 }
981
982 /* Must have a ccode */
983 if (resp_len < sizeof(struct pldm_msg_hdr) + 1) {
984 return -EINVAL;
985 }
986 resp_payload_len = resp_len - sizeof(struct pldm_msg_hdr);
987
988 if (fd->req.state != PLDM_FD_REQ_SENT) {
989 // No response was expected
990 return -EPROTO;
991 }
992
993 if (fd->req.instance_id != resp->hdr.instance_id) {
994 // Response wasn't for the expected request
995 return -EPROTO;
996 }
997 if (fd->req.command != resp->hdr.command) {
998 // Response wasn't for the expected request
999 return -EPROTO;
1000 }
1001
1002 fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1003
1004 switch (resp->hdr.command) {
1005 case PLDM_REQUEST_FIRMWARE_DATA:
1006 return pldm_fd_handle_fwdata_resp(fd, resp, resp_payload_len);
1007 break;
1008 case PLDM_TRANSFER_COMPLETE:
1009 return pldm_fd_handle_transfer_complete_resp(fd, resp,
1010 resp_payload_len);
1011 break;
1012 case PLDM_VERIFY_COMPLETE:
1013 return pldm_fd_handle_verify_complete_resp(fd, resp,
1014 resp_payload_len);
1015 break;
1016 case PLDM_APPLY_COMPLETE:
1017 return pldm_fd_handle_apply_complete_resp(fd, resp,
1018 resp_payload_len);
1019 break;
1020 default:
1021 /* Unsolicited response. Already compared to command above */
1022 assert(false);
1023 return -EINVAL;
1024 }
1025}
1026
1027LIBPLDM_CC_NONNULL
1028static int pldm_fd_progress_download(struct pldm_fd *fd, struct pldm_msg *req,
1029 size_t *req_payload_len)
1030{
1031 uint8_t instance_id;
1032 struct pldm_fd_download *dl;
1033 int rc;
1034
1035 if (!pldm_fd_req_should_send(fd)) {
1036 /* Nothing to do */
1037 *req_payload_len = 0;
1038 return 0;
1039 }
1040
1041 instance_id = pldm_fd_req_next_instance(&fd->req);
1042 dl = &fd->specific.download;
1043 if (fd->req.complete) {
1044 /* Send TransferComplete */
1045 rc = encode_transfer_complete_req(instance_id, fd->req.result,
1046 req, req_payload_len);
1047 } else {
1048 /* Send a new RequestFirmwareData */
1049 const struct pldm_request_firmware_data_req req_params = {
1050 .offset = dl->offset,
1051 .length = pldm_fd_fwdata_size(fd),
1052 };
1053
1054 rc = encode_request_firmware_data_req(instance_id, &req_params,
1055 req, req_payload_len);
1056 }
1057
1058 if (rc) {
1059 return rc;
1060 }
1061
1062 /* Wait for response */
1063 fd->req.state = PLDM_FD_REQ_SENT;
1064 fd->req.instance_id = req->hdr.instance_id;
1065 fd->req.command = req->hdr.command;
1066 fd->req.sent_time = pldm_fd_now(fd);
1067
1068 return 0;
1069}
1070
1071LIBPLDM_CC_NONNULL
1072static int pldm_fd_progress_verify(struct pldm_fd *fd, struct pldm_msg *req,
1073 size_t *req_payload_len)
1074{
1075 uint8_t instance_id;
1076 int rc;
1077
1078 if (!pldm_fd_req_should_send(fd)) {
1079 /* Nothing to do */
1080 *req_payload_len = 0;
1081 return 0;
1082 }
1083
1084 if (!fd->req.complete) {
1085 bool pending = false;
1086 uint8_t res;
1087 res = fd->ops->verify(fd->ops_ctx, &fd->update_comp, &pending,
1088 &fd->specific.verify.progress_percent);
1089 if (pending) {
1090 if (res == PLDM_FWUP_VERIFY_SUCCESS) {
1091 /* Return without a VerifyComplete request.
1092 * Will call verify() again on next call */
1093 *req_payload_len = 0;
1094 return 0;
1095 }
1096 /* This is an API infraction by the implementer, return a distinctive failure */
1097 res = PLDM_FWUP_VENDOR_VERIFY_RESULT_RANGE_MAX;
1098 }
1099 fd->req.result = res;
1100 fd->req.complete = true;
1101 }
1102
1103 instance_id = pldm_fd_req_next_instance(&fd->req);
1104 rc = encode_verify_complete_req(instance_id, fd->req.result, req,
1105 req_payload_len);
1106 if (rc) {
1107 return rc;
1108 }
1109
1110 /* Wait for response */
1111 fd->req.state = PLDM_FD_REQ_SENT;
1112 fd->req.instance_id = req->hdr.instance_id;
1113 fd->req.command = req->hdr.command;
1114 fd->req.sent_time = pldm_fd_now(fd);
1115
1116 return 0;
1117}
1118
1119LIBPLDM_CC_NONNULL
1120static int pldm_fd_progress_apply(struct pldm_fd *fd, struct pldm_msg *req,
1121 size_t *req_payload_len)
1122{
1123 uint8_t instance_id;
1124 int rc;
1125
1126 if (!pldm_fd_req_should_send(fd)) {
1127 /* Nothing to do */
1128 *req_payload_len = 0;
1129 return 0;
1130 }
1131
1132 if (!fd->req.complete) {
1133 bool pending = false;
1134 uint8_t res;
1135 res = fd->ops->apply(fd->ops_ctx, &fd->update_comp, &pending,
1136 &fd->specific.apply.progress_percent);
1137 if (pending) {
1138 if (res == PLDM_FWUP_APPLY_SUCCESS) {
1139 /* Return without a ApplyComplete request.
1140 * Will call apply() again on next call */
1141 *req_payload_len = 0;
1142 return 0;
1143 }
1144 /* This is an API infraction by the implementer, return a distinctive failure */
1145 res = PLDM_FWUP_VENDOR_APPLY_RESULT_RANGE_MAX;
1146 }
1147 fd->req.result = res;
1148 fd->req.complete = true;
1149 if (fd->req.result ==
1150 PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD) {
1151 /* modified activation method isn't currently handled */
1152 fd->req.result = PLDM_FWUP_APPLY_SUCCESS;
1153 }
1154 }
1155
1156 instance_id = pldm_fd_req_next_instance(&fd->req);
1157 const struct pldm_apply_complete_req req_data = {
1158 .apply_result = fd->req.result,
1159 .comp_activation_methods_modification = { 0 },
1160 };
1161 rc = encode_apply_complete_req(instance_id, &req_data, req,
1162 req_payload_len);
1163 if (rc) {
1164 return rc;
1165 }
1166
1167 /* Wait for response */
1168 fd->req.state = PLDM_FD_REQ_SENT;
1169 fd->req.instance_id = req->hdr.instance_id;
1170 fd->req.command = req->hdr.command;
1171 fd->req.sent_time = pldm_fd_now(fd);
1172
1173 return 0;
1174}
1175
1176LIBPLDM_ABI_TESTING
1177struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx)
1178{
1179 struct pldm_fd *fd = malloc(sizeof(*fd));
1180 if (fd) {
1181 if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx) == 0) {
1182 return fd;
1183 }
1184 free(fd);
1185 fd = NULL;
1186 }
1187 return fd;
1188}
1189
1190LIBPLDM_ABI_TESTING
1191int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
1192 const struct pldm_fd_ops *ops, void *ops_ctx)
1193{
1194 if (fd == NULL || ops == NULL) {
1195 return -EINVAL;
1196 }
1197
1198 if (ops->device_identifiers == NULL || ops->components == NULL ||
1199 ops->imageset_versions == NULL || ops->update_component == NULL ||
1200 ops->transfer_size == NULL || ops->firmware_data == NULL ||
1201 ops->verify == NULL || ops->activate == NULL ||
1202 ops->cancel_update_component == NULL || ops->now == NULL) {
1203 return -EINVAL;
1204 }
1205
1206 if (pldm_fd_size < sizeof(struct pldm_fd)) {
1207 /* Safety check that sufficient storage was provided for *fd,
1208 * in case PLDM_SIZEOF_PLDM_FD is incorrect */
1209 return -EINVAL;
1210 }
1211 memset(fd, 0x0, sizeof(*fd));
1212 fd->ops = ops;
1213 fd->ops_ctx = ops_ctx;
1214 fd->fd_t1_timeout = DEFAULT_FD_T1_TIMEOUT;
1215 fd->fd_t2_retry_time = DEFAULT_FD_T2_RETRY_TIME;
1216
1217 return 0;
1218}
1219
1220LIBPLDM_ABI_TESTING
1221int pldm_fd_handle_msg(struct pldm_fd *fd, pldm_tid_t remote_address,
1222 const void *in_msg, size_t in_len, void *out_msg,
1223 size_t *out_len)
1224{
1225 size_t req_payload_len;
1226 size_t resp_payload_len;
1227 struct pldm_header_info hdr;
1228 const struct pldm_msg *req = in_msg;
1229 struct pldm_msg *resp = out_msg;
1230 uint8_t rc;
1231
1232 if (fd == NULL || in_msg == NULL || out_msg == NULL ||
1233 out_len == NULL) {
1234 return -EINVAL;
1235 }
1236
1237 if (in_len < sizeof(struct pldm_msg_hdr)) {
1238 return -EOVERFLOW;
1239 }
1240 req_payload_len = in_len - sizeof(struct pldm_msg_hdr);
1241
1242 rc = unpack_pldm_header(&req->hdr, &hdr);
1243 if (rc != PLDM_SUCCESS) {
1244 return -EINVAL;
1245 }
1246
1247 if (hdr.pldm_type != PLDM_FWUP) {
1248 /* Caller should not have passed non-pldmfw */
1249 return -ENOMSG;
1250 }
1251
1252 if (hdr.msg_type == PLDM_RESPONSE) {
1253 *out_len = 0;
1254 return pldm_fd_handle_resp(fd, remote_address, in_msg, in_len);
1255 }
1256
1257 if (hdr.msg_type != PLDM_REQUEST) {
1258 return -EPROTO;
1259 }
1260
1261 /* Space for header plus completion code */
1262 if (*out_len < sizeof(struct pldm_msg_hdr) + 1) {
1263 return -EOVERFLOW;
1264 }
1265 resp_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1266
1267 /* Check address */
1268 switch (hdr.command) {
1269 /* Information or cancel commands are always allowed */
1270 case PLDM_QUERY_DEVICE_IDENTIFIERS:
1271 case PLDM_GET_FIRMWARE_PARAMETERS:
1272 case PLDM_GET_STATUS:
1273 case PLDM_CANCEL_UPDATE:
1274 case PLDM_QUERY_DOWNSTREAM_DEVICES:
1275 case PLDM_QUERY_DOWNSTREAM_IDENTIFIERS:
1276 case PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS:
1277 /* Request Update handler will set address */
1278 case PLDM_REQUEST_UPDATE:
1279 break;
1280 default:
1281 /* Requests must come from the same address that requested the update */
1282 if (!fd->ua_address_set || remote_address != fd->ua_address) {
1283 return pldm_fd_reply_cc(PLDM_ERROR_NOT_READY, &hdr,
1284 resp, &resp_payload_len);
1285 }
1286 }
1287
1288 /* Update timeout */
1289 switch (hdr.command) {
1290 case PLDM_REQUEST_UPDATE:
1291 case PLDM_PASS_COMPONENT_TABLE:
1292 case PLDM_UPDATE_COMPONENT:
1293 case PLDM_CANCEL_UPDATE:
1294 fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1295 break;
1296 default:
1297 break;
1298 }
1299
1300 /* Dispatch command */
1301 switch (hdr.command) {
1302 case PLDM_QUERY_DEVICE_IDENTIFIERS:
1303 rc = pldm_fd_qdi(fd, &hdr, req, req_payload_len, resp,
1304 &resp_payload_len);
1305 break;
1306 case PLDM_GET_FIRMWARE_PARAMETERS:
1307 rc = pldm_fd_fw_param(fd, &hdr, req, req_payload_len, resp,
1308 &resp_payload_len);
1309 break;
1310 case PLDM_REQUEST_UPDATE:
1311 rc = pldm_fd_request_update(fd, &hdr, req, req_payload_len,
1312 resp, &resp_payload_len,
1313 remote_address);
1314 break;
1315 case PLDM_PASS_COMPONENT_TABLE:
1316 rc = pldm_fd_pass_comp(fd, &hdr, req, req_payload_len, resp,
1317 &resp_payload_len);
1318 break;
1319 case PLDM_UPDATE_COMPONENT:
1320 rc = pldm_fd_update_comp(fd, &hdr, req, req_payload_len, resp,
1321 &resp_payload_len);
1322 break;
1323 case PLDM_GET_STATUS:
1324 rc = pldm_fd_get_status(fd, &hdr, req, req_payload_len, resp,
1325 &resp_payload_len);
1326 break;
1327 case PLDM_CANCEL_UPDATE_COMPONENT:
1328 rc = pldm_fd_cancel_update_comp(fd, &hdr, req, req_payload_len,
1329 resp, &resp_payload_len);
1330 break;
1331 case PLDM_CANCEL_UPDATE:
1332 rc = pldm_fd_cancel_update(fd, &hdr, req, req_payload_len, resp,
1333 &resp_payload_len);
1334 break;
1335 case PLDM_ACTIVATE_FIRMWARE:
1336 rc = pldm_fd_activate_firmware(fd, &hdr, req, req_payload_len,
1337 resp, &resp_payload_len);
1338 break;
1339 default:
1340 rc = pldm_fd_reply_cc(PLDM_ERROR_UNSUPPORTED_PLDM_CMD, &hdr,
1341 resp, &resp_payload_len);
1342 }
1343
1344 if (rc == 0) {
1345 *out_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
1346 }
1347
1348 return rc;
1349}
1350
1351LIBPLDM_ABI_TESTING
1352int pldm_fd_progress(struct pldm_fd *fd, void *out_msg, size_t *out_len,
1353 pldm_tid_t *address)
1354{
1355 size_t req_payload_len;
1356 struct pldm_msg *req = out_msg;
1357 int rc = -EINVAL;
1358
1359 if (fd == NULL || out_msg == NULL || out_len == NULL) {
1360 return -EINVAL;
1361 }
1362
1363 /* Space for header */
1364 if (*out_len < sizeof(struct pldm_msg_hdr)) {
1365 return -EOVERFLOW;
1366 }
1367 req_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1368 *out_len = 0;
1369
1370 switch (fd->state) {
1371 case PLDM_FD_STATE_DOWNLOAD:
1372 rc = pldm_fd_progress_download(fd, req, &req_payload_len);
1373 break;
1374 case PLDM_FD_STATE_VERIFY:
1375 rc = pldm_fd_progress_verify(fd, req, &req_payload_len);
1376 break;
1377 case PLDM_FD_STATE_APPLY:
1378 rc = pldm_fd_progress_apply(fd, req, &req_payload_len);
1379 break;
1380 case PLDM_FD_STATE_IDLE:
1381 req_payload_len = 0;
1382 break;
1383 default:
1384 req_payload_len = 0;
1385 // Other Update Mode states have a timeout
1386 if ((pldm_fd_now(fd) - fd->update_timestamp_fd_t1) >
1387 fd->fd_t1_timeout) {
1388 pldm_fd_maybe_cancel_component(fd);
1389 pldm_fd_idle_timeout(fd);
1390 }
1391 break;
1392 }
1393
1394 if (rc == 0 && fd->ua_address_set && req_payload_len > 0) {
1395 *out_len = req_payload_len + sizeof(struct pldm_msg_hdr);
1396 *address = fd->ua_address;
1397 }
1398
1399 return rc;
1400}
1401
1402LIBPLDM_ABI_TESTING
1403int pldm_fd_set_update_idle_timeout(struct pldm_fd *fd, uint32_t time)
1404{
1405 if (fd == NULL) {
1406 return -EINVAL;
1407 }
1408
1409 fd->fd_t1_timeout = time;
1410 return 0;
1411}
1412
1413LIBPLDM_ABI_TESTING
1414int pldm_fd_set_request_retry_time(struct pldm_fd *fd, uint32_t time)
1415{
1416 if (fd == NULL) {
1417 return -EINVAL;
1418 }
1419
1420 fd->fd_t2_retry_time = time;
1421 return 0;
1422}