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