blob: 096703388e05b6908c2c7c70110b7c78a4cf4800 [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;
Andrew Jefferya1896962025-03-03 21:41:25 +1030279 PLDM_MSGBUF_DEFINE_P(buf);
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800280 int rc;
281
282 /* No request data */
283 if (req_payload_len != PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES) {
284 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
285 resp_payload_len);
286 }
287
288 /* Retrieve platform-specific data */
289 rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
290 if (rc) {
291 return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
292 resp_payload_len);
293 }
294
295 rc = pldm_msgbuf_init_errno(buf, 0, resp->payload, *resp_payload_len);
296 if (rc) {
297 return rc;
298 }
299
300 /* Add the fixed parameters */
301 {
302 struct pldm_get_firmware_parameters_resp_full fwp = {
303 .completion_code = PLDM_SUCCESS,
304 // TODO defaulted to 0, could have a callback.
305 .capabilities_during_update = { 0 },
306 .comp_count = entry_count,
307 };
308 /* fill active and pending strings */
309 rc = fd->ops->imageset_versions(
310 fd->ops_ctx, &fwp.active_comp_image_set_ver_str,
311 &fwp.pending_comp_image_set_ver_str);
312 if (rc) {
Andrew Jefferya1896962025-03-03 21:41:25 +1030313 return pldm_msgbuf_discard(
314 buf, pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
315 resp_payload_len));
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800316 }
317
318 size_t len = buf->remaining;
319 rc = encode_get_firmware_parameters_resp(hdr->instance, &fwp,
320 resp, &len);
321 if (rc) {
Andrew Jefferya1896962025-03-03 21:41:25 +1030322 return pldm_msgbuf_discard(
323 buf, pldm_fd_reply_errno(rc, hdr, resp,
324 resp_payload_len));
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800325 }
326 rc = pldm_msgbuf_skip(buf, len);
327 if (rc) {
Andrew Jefferya1896962025-03-03 21:41:25 +1030328 return pldm_msgbuf_discard(buf, rc);
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800329 }
330 }
331
332 /* Add the component table entries */
333 for (uint16_t i = 0; i < entry_count; i++) {
334 const struct pldm_firmware_component_standalone *e = entries[i];
335 void *out = NULL;
336 size_t len;
337
338 struct pldm_component_parameter_entry_full comp = {
339 .comp_classification = e->comp_classification,
340 .comp_identifier = e->comp_identifier,
341 .comp_classification_index =
342 e->comp_classification_index,
343
344 .active_ver = e->active_ver,
345 .pending_ver = e->pending_ver,
346
347 .comp_activation_methods = e->comp_activation_methods,
348 .capabilities_during_update =
349 e->capabilities_during_update,
350 };
351
352 if (pldm_msgbuf_peek_remaining(buf, &out, &len)) {
Andrew Jefferya1896962025-03-03 21:41:25 +1030353 return pldm_msgbuf_discard(buf, rc);
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800354 }
Andrew Jefferya1896962025-03-03 21:41:25 +1030355
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800356 rc = encode_get_firmware_parameters_resp_comp_entry(&comp, out,
357 &len);
358 if (rc) {
Andrew Jefferya1896962025-03-03 21:41:25 +1030359 return pldm_msgbuf_discard(
360 buf, pldm_fd_reply_errno(rc, hdr, resp,
361 resp_payload_len));
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800362 }
Andrew Jefferya1896962025-03-03 21:41:25 +1030363
364 pldm_msgbuf_skip(buf, len);
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800365 }
366
Andrew Jeffery70d21c92025-03-05 12:59:42 +1030367 return pldm_msgbuf_complete_used(buf, *resp_payload_len,
368 resp_payload_len);
Matt Johnston8c2bfb12024-11-07 15:31:32 +0800369}
370
371LIBPLDM_CC_NONNULL
372static int pldm_fd_request_update(struct pldm_fd *fd,
373 const struct pldm_header_info *hdr,
374 const struct pldm_msg *req,
375 size_t req_payload_len, struct pldm_msg *resp,
376 size_t *resp_payload_len, uint8_t address)
377{
378 struct pldm_request_update_req_full upd;
379 const struct pldm_request_update_resp resp_data = {
380 .fd_meta_data_len = 0,
381 .fd_will_send_pkg_data = 0,
382 };
383 int rc;
384
385 if (fd->state != PLDM_FD_STATE_IDLE) {
386 return pldm_fd_reply_cc(PLDM_FWUP_ALREADY_IN_UPDATE_MODE, hdr,
387 resp, resp_payload_len);
388 }
389
390 rc = decode_request_update_req(req, req_payload_len, &upd);
391 if (rc) {
392 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
393 }
394
395 /* No metadata nor pkg data */
396 rc = encode_request_update_resp(hdr->instance, &resp_data, resp,
397 resp_payload_len);
398 if (rc) {
399 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
400 }
401
402 // TODO pass num_of_comp and image_set_ver to application?
403
404 fd->max_transfer =
405 fd->ops->transfer_size(fd->ops_ctx, upd.max_transfer_size);
406 if (fd->max_transfer > upd.max_transfer_size) {
407 /* Limit to UA's size */
408 fd->max_transfer = upd.max_transfer_size;
409 }
410 if (fd->max_transfer < PLDM_FWUP_BASELINE_TRANSFER_SIZE) {
411 /* Don't let it be zero, that will loop forever */
412 fd->max_transfer = PLDM_FWUP_BASELINE_TRANSFER_SIZE;
413 }
414 fd->ua_address = address;
415 fd->ua_address_set = true;
416
417 pldm_fd_set_state(fd, PLDM_FD_STATE_LEARN_COMPONENTS);
418
419 return 0;
420}
421
422/* wrapper around ops->cancel, will only run ops->cancel when a component update is
423 * active */
424LIBPLDM_CC_NONNULL
425static void pldm_fd_maybe_cancel_component(struct pldm_fd *fd)
426{
427 bool cancel = false;
428
429 switch (fd->state) {
430 case PLDM_FD_STATE_DOWNLOAD:
431 case PLDM_FD_STATE_VERIFY:
432 cancel = true;
433 break;
434 case PLDM_FD_STATE_APPLY:
435 /* In apply state, once the application ops->apply() has completed
436 * successfully the component is no longer in update state.
437 * In that case the cancel should not be forwarded to the application.
438 * This can occur if a cancel is received while waiting for the
439 * response to a success ApplyComplete. */
440 cancel = !(fd->req.complete &&
441 fd->req.result == PLDM_FWUP_APPLY_SUCCESS);
442 break;
443 default:
444 break;
445 }
446
447 if (cancel) {
448 /* Call the platform handler for the current component in progress */
449 fd->ops->cancel_update_component(fd->ops_ctx, &fd->update_comp);
450 }
451}
452
453/* Wrapper around ops->update_component() that first checks that the component
454 * is in the list returned from ops->components() */
455LIBPLDM_CC_NONNULL
456static enum pldm_component_response_codes pldm_fd_check_update_component(
457 struct pldm_fd *fd, bool update,
458 const struct pldm_firmware_update_component *comp)
459{
460 bool found;
461 uint16_t entry_count;
462 int rc;
463
464 const struct pldm_firmware_component_standalone **entries;
465 rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
466 if (rc) {
467 return PLDM_CRC_COMP_NOT_SUPPORTED;
468 }
469
470 found = false;
471 for (uint16_t i = 0; i < entry_count; i++) {
472 if (entries[i]->comp_classification ==
473 comp->comp_classification &&
474 entries[i]->comp_identifier == comp->comp_identifier &&
475 entries[i]->comp_classification_index ==
476 comp->comp_classification_index) {
477 found = true;
478 break;
479 }
480 }
481 if (found) {
482 return fd->ops->update_component(fd->ops_ctx, update, comp);
483 }
484 return PLDM_CRC_COMP_NOT_SUPPORTED;
485}
486
487LIBPLDM_CC_NONNULL
488static int pldm_fd_pass_comp(struct pldm_fd *fd,
489 const struct pldm_header_info *hdr,
490 const struct pldm_msg *req, size_t req_payload_len,
491 struct pldm_msg *resp, size_t *resp_payload_len)
492{
493 struct pldm_pass_component_table_req_full pcomp;
494 uint8_t comp_response_code;
495 int rc;
496
497 if (fd->state != PLDM_FD_STATE_LEARN_COMPONENTS) {
498 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
499 hdr, resp, resp_payload_len);
500 }
501
502 /* fd->update_comp is used as temporary storage during PassComponent validation */
503 /* Some portions are unused for PassComponentTable */
504 fd->update_comp.comp_image_size = 0;
505 fd->update_comp.update_option_flags.value = 0;
506
507 rc = decode_pass_component_table_req(req, req_payload_len, &pcomp);
508 if (rc) {
509 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
510 }
511
512 fd->update_comp.comp_classification = pcomp.comp_classification;
513 fd->update_comp.comp_identifier = pcomp.comp_identifier;
514 fd->update_comp.comp_classification_index =
515 pcomp.comp_classification_index;
516 fd->update_comp.comp_comparison_stamp = pcomp.comp_comparison_stamp;
517 memcpy(&fd->update_comp.version, &pcomp.version, sizeof(pcomp.version));
518
519 comp_response_code =
520 pldm_fd_check_update_component(fd, false, &fd->update_comp);
521
522 const struct pldm_pass_component_table_resp resp_data = {
523 /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
524 .comp_resp = (comp_response_code != 0),
525 .comp_resp_code = comp_response_code,
526 };
527
528 rc = encode_pass_component_table_resp(hdr->instance, &resp_data, resp,
529 resp_payload_len);
530 if (rc) {
531 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
532 }
533
534 if (pcomp.transfer_flag & PLDM_END) {
535 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
536 }
537
538 return 0;
539}
540
541LIBPLDM_CC_NONNULL
542static int pldm_fd_update_comp(struct pldm_fd *fd,
543 const struct pldm_header_info *hdr,
544 const struct pldm_msg *req,
545 size_t req_payload_len, struct pldm_msg *resp,
546 size_t *resp_payload_len)
547{
548 struct pldm_update_component_req_full up;
549 uint8_t comp_response_code;
550 int rc;
551
552 if (fd->state != PLDM_FD_STATE_READY_XFER) {
553 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
554 hdr, resp, resp_payload_len);
555 }
556
557 rc = decode_update_component_req(req, req_payload_len, &up);
558 if (rc) {
559 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
560 }
561
562 /* Store update_comp to pass to further callbacks. This persists
563 * until the component update completes or is cancelled */
564 fd->update_comp.comp_classification = up.comp_classification;
565 fd->update_comp.comp_identifier = up.comp_identifier;
566 fd->update_comp.comp_classification_index =
567 up.comp_classification_index;
568 fd->update_comp.comp_comparison_stamp = up.comp_comparison_stamp;
569 fd->update_comp.comp_image_size = up.comp_image_size;
570 fd->update_comp.update_option_flags = up.update_option_flags;
571 memcpy(&fd->update_comp.version, &up.version, sizeof(up.version));
572
573 comp_response_code =
574 pldm_fd_check_update_component(fd, true, &fd->update_comp);
575
576 // Mask to only the "Force Update" flag, others are not handled.
577 bitfield32_t update_flags = {
578 .bits.bit0 = fd->update_comp.update_option_flags.bits.bit0
579 };
580
581 const struct pldm_update_component_resp resp_data = {
582 /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
583 .comp_compatibility_resp = (comp_response_code != 0),
584 .comp_compatibility_resp_code = comp_response_code,
585 .update_option_flags_enabled = update_flags,
586 .time_before_req_fw_data = 0,
587 };
588
589 rc = encode_update_component_resp(hdr->instance, &resp_data, resp,
590 resp_payload_len);
591 if (rc) {
592 /* Encoding response failed */
593 if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
594 /* Inform the application of cancellation. Call it directly
595 * rather than going through pldm_fd_maybe_cancel_component() */
596 fd->ops->cancel_update_component(fd->ops_ctx,
597 &fd->update_comp);
598 }
599 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
600 }
601
602 /* Set up download state */
603 if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
604 memset(&fd->specific, 0x0, sizeof(fd->specific));
605 fd->update_flags = update_flags;
606 fd->req.state = PLDM_FD_REQ_READY;
607 fd->req.complete = false;
608 pldm_fd_set_state(fd, PLDM_FD_STATE_DOWNLOAD);
609 }
610
611 return 0;
612}
613
614LIBPLDM_CC_NONNULL
615static int pldm_fd_get_status(struct pldm_fd *fd,
616 const struct pldm_header_info *hdr,
617 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
618 size_t req_payload_len, struct pldm_msg *resp,
619 size_t *resp_payload_len)
620{
621 int rc;
622
623 /* No request data */
624 if (req_payload_len != PLDM_GET_STATUS_REQ_BYTES) {
625 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
626 resp_payload_len);
627 }
628
629 /* Defaults */
630 struct pldm_get_status_resp st = {
631 .current_state = fd->state,
632 .previous_state = fd->prev_state,
633 .progress_percent = PROGRESS_PERCENT_NOT_SUPPORTED,
634 };
635
636 pldm_fd_get_aux_state(fd, &st.aux_state, &st.aux_state_status);
637
638 switch (fd->state) {
639 case PLDM_FD_STATE_IDLE:
640 st.reason_code = fd->reason;
641 break;
642 case PLDM_FD_STATE_DOWNLOAD:
643 if (fd->update_comp.comp_image_size > 0) {
644 uint32_t one_percent =
645 fd->update_comp.comp_image_size / 100;
646 if (fd->update_comp.comp_image_size % 100 != 0) {
647 one_percent += 1;
648 }
649 st.progress_percent =
650 (fd->specific.download.offset / one_percent);
651 }
652 st.update_option_flags_enabled = fd->update_flags;
653 break;
654 case PLDM_FD_STATE_VERIFY:
655 st.update_option_flags_enabled = fd->update_flags;
656 st.progress_percent = fd->specific.verify.progress_percent;
657 break;
658 case PLDM_FD_STATE_APPLY:
659 st.update_option_flags_enabled = fd->update_flags;
660 st.progress_percent = fd->specific.apply.progress_percent;
661 break;
662 default:
663 break;
664 }
665
666 rc = encode_get_status_resp(hdr->instance, &st, resp, resp_payload_len);
667 if (rc) {
668 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
669 }
670
671 return 0;
672}
673
674LIBPLDM_CC_NONNULL
675static int pldm_fd_cancel_update_comp(
676 struct pldm_fd *fd, const struct pldm_header_info *hdr,
677 const struct pldm_msg *req LIBPLDM_CC_UNUSED, size_t req_payload_len,
678 struct pldm_msg *resp, size_t *resp_payload_len)
679{
680 int rc;
681
682 /* No request data */
683 if (req_payload_len != PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES) {
684 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
685 resp_payload_len);
686 }
687
688 switch (fd->state) {
689 case PLDM_FD_STATE_DOWNLOAD:
690 case PLDM_FD_STATE_VERIFY:
691 case PLDM_FD_STATE_APPLY:
692 break;
693 default:
694 return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
695 resp_payload_len);
696 }
697
698 /* No response payload */
699 rc = pldm_fd_reply_cc(PLDM_SUCCESS, hdr, resp, resp_payload_len);
700 if (rc) {
701 return rc;
702 }
703
704 pldm_fd_maybe_cancel_component(fd);
705 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
706
707 return 0;
708}
709
710LIBPLDM_CC_NONNULL
711static int pldm_fd_cancel_update(struct pldm_fd *fd,
712 const struct pldm_header_info *hdr,
713 const struct pldm_msg *req LIBPLDM_CC_UNUSED,
714 size_t req_payload_len, struct pldm_msg *resp,
715 size_t *resp_payload_len)
716{
717 int rc;
718
719 /* No request data */
720 if (req_payload_len != PLDM_CANCEL_UPDATE_REQ_BYTES) {
721 return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
722 resp_payload_len);
723 }
724
725 if (fd->state == PLDM_FD_STATE_IDLE) {
726 return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
727 resp_payload_len);
728 }
729
730 /* Assume non_functioning_component_indication = False, in future
731 * could add a platform callback */
732 const struct pldm_cancel_update_resp resp_data = {
733 .non_functioning_component_indication = 0,
734 .non_functioning_component_bitmap = 0,
735 };
736 rc = encode_cancel_update_resp(hdr->instance, &resp_data, resp,
737 resp_payload_len);
738 if (rc) {
739 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
740 }
741
742 pldm_fd_maybe_cancel_component(fd);
743 pldm_fd_set_idle(fd, PLDM_FD_CANCEL_UPDATE);
744
745 return 0;
746}
747
748LIBPLDM_CC_NONNULL
749static int pldm_fd_activate_firmware(struct pldm_fd *fd,
750 const struct pldm_header_info *hdr,
751 const struct pldm_msg *req,
752 size_t req_payload_len,
753 struct pldm_msg *resp,
754 size_t *resp_payload_len)
755{
756 uint16_t estimated_time;
757 uint8_t ccode;
758 int rc;
759 bool self_contained;
760
761 rc = decode_activate_firmware_req(req, req_payload_len,
762 &self_contained);
763 if (rc) {
764 return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
765 }
766
767 if (fd->state != PLDM_FD_STATE_READY_XFER) {
768 return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
769 hdr, resp, resp_payload_len);
770 }
771
772 estimated_time = 0;
773 ccode = fd->ops->activate(fd->ops_ctx, self_contained, &estimated_time);
774
775 if (ccode == PLDM_SUCCESS ||
776 ccode == PLDM_FWUP_ACTIVATION_NOT_REQUIRED) {
777 /* Transition through states so that the prev_state is correct */
778 pldm_fd_set_state(fd, PLDM_FD_STATE_ACTIVATE);
779 pldm_fd_set_idle(fd, PLDM_FD_ACTIVATE_FW);
780 }
781
782 if (ccode == PLDM_SUCCESS) {
783 const struct pldm_activate_firmware_resp resp_data = {
784 .estimated_time_activation = estimated_time,
785 };
786 rc = encode_activate_firmware_resp(hdr->instance, &resp_data,
787 resp, resp_payload_len);
788 if (rc) {
789 return pldm_fd_reply_errno(rc, hdr, resp,
790 resp_payload_len);
791 }
792 } else {
793 return pldm_fd_reply_cc(ccode, hdr, resp, resp_payload_len);
794 }
795
796 return 0;
797}
798
799LIBPLDM_CC_NONNULL
800static uint32_t pldm_fd_fwdata_size(struct pldm_fd *fd)
801{
802 uint32_t size;
803
804 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
805 assert(false);
806 return 0;
807 }
808
809 if (fd->specific.download.offset > fd->update_comp.comp_image_size) {
810 assert(false);
811 return 0;
812 }
813 size = fd->update_comp.comp_image_size - fd->specific.download.offset;
814
815 if (size > fd->max_transfer) {
816 size = fd->max_transfer;
817 }
818 return size;
819}
820
821LIBPLDM_CC_NONNULL
822static int pldm_fd_handle_fwdata_resp(struct pldm_fd *fd,
823 const struct pldm_msg *resp,
824 size_t resp_payload_len)
825{
826 struct pldm_fd_download *dl;
827 uint32_t fwdata_size;
828 uint8_t res;
829
830 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
831 return -EPROTO;
832 }
833
834 if (fd->req.state != PLDM_FD_REQ_SENT) {
835 /* Not waiting for a response, ignore it */
836 return -EPROTO;
837 }
838
839 dl = &fd->specific.download;
840 if (fd->req.complete) {
841 /* Received data after completion */
842 return -EPROTO;
843 }
844
845 switch (resp->payload[0]) {
846 case PLDM_SUCCESS:
847 break;
848 case PLDM_FWUP_RETRY_REQUEST_FW_DATA:
849 /* Just return, let the retry timer send another request later */
850 return 0;
851 default:
852 /* Send a TransferComplete failure */
853 fd->req.state = PLDM_FD_REQ_READY;
854 fd->req.complete = true;
855 fd->req.result = PLDM_FWUP_FD_ABORTED_TRANSFER;
856 return 0;
857 }
858
859 /* Handle the received data */
860
861 fwdata_size = pldm_fd_fwdata_size(fd);
862 if (resp_payload_len != fwdata_size + 1) {
863 /* Data is incorrect size. Could indicate MCTP corruption, drop it
864 * and let retry timer handle it */
865 return -EOVERFLOW;
866 }
867
868 /* Check pldm_fd_fwdata_size calculation, should not fail */
869 if (dl->offset + fwdata_size < dl->offset ||
870 dl->offset + fwdata_size > fd->update_comp.comp_image_size) {
871 assert(false);
872 return -EINVAL;
873 }
874
875 /* Provide the data chunk to the device */
876 res = fd->ops->firmware_data(fd->ops_ctx, dl->offset, &resp->payload[1],
877 fwdata_size, &fd->update_comp);
878
879 fd->req.state = PLDM_FD_REQ_READY;
880 if (res == PLDM_FWUP_TRANSFER_SUCCESS) {
881 /* Move to next offset */
882 dl->offset += fwdata_size;
883 if (dl->offset == fd->update_comp.comp_image_size) {
884 /* Mark as complete, next progress() call will send the TransferComplete request */
885 fd->req.complete = true;
886 fd->req.result = PLDM_FWUP_TRANSFER_SUCCESS;
887 }
888 } else {
889 /* Pass the callback error as the TransferResult */
890 fd->req.complete = true;
891 fd->req.result = res;
892 }
893
894 return 0;
895}
896
897LIBPLDM_CC_NONNULL
898static int pldm_fd_handle_transfer_complete_resp(
899 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
900 size_t resp_payload_len LIBPLDM_CC_UNUSED)
901{
902 if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
903 return -EPROTO;
904 }
905
906 if (fd->req.state != PLDM_FD_REQ_SENT) {
907 /* Not waiting for a response, ignore it */
908 return -EPROTO;
909 }
910
911 if (!fd->req.complete) {
912 /* Were waiting for RequestFirmwareData instead, ignore it */
913 return -EPROTO;
914 }
915
916 /* Disregard the response completion code */
917
918 /* Next state depends whether the transfer succeeded */
919 if (fd->req.result == PLDM_FWUP_TRANSFER_SUCCESS) {
920 /* Switch to Verify */
921 memset(&fd->specific, 0x0, sizeof(fd->specific));
922 fd->specific.verify.progress_percent =
923 PROGRESS_PERCENT_NOT_SUPPORTED;
924 fd->req.state = PLDM_FD_REQ_READY;
925 fd->req.complete = false;
926 pldm_fd_set_state(fd, PLDM_FD_STATE_VERIFY);
927 } else {
928 /* Wait for UA to cancel */
929 fd->req.state = PLDM_FD_REQ_FAILED;
930 }
931 return 0;
932}
933
934LIBPLDM_CC_NONNULL
935static int pldm_fd_handle_verify_complete_resp(
936 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
937 size_t resp_payload_len LIBPLDM_CC_UNUSED)
938{
939 if (fd->state != PLDM_FD_STATE_VERIFY) {
940 return -EPROTO;
941 }
942
943 if (fd->req.state != PLDM_FD_REQ_SENT) {
944 /* Not waiting for a response, ignore it */
945 return -EPROTO;
946 }
947
948 assert(fd->req.complete);
949
950 /* Disregard the response completion code */
951
952 /* Next state depends whether the verify succeeded */
953 if (fd->req.result == PLDM_FWUP_VERIFY_SUCCESS) {
954 /* Switch to Apply */
955 memset(&fd->specific, 0x0, sizeof(fd->specific));
956 fd->specific.apply.progress_percent =
957 PROGRESS_PERCENT_NOT_SUPPORTED;
958 fd->req.state = PLDM_FD_REQ_READY;
959 fd->req.complete = false;
960 pldm_fd_set_state(fd, PLDM_FD_STATE_APPLY);
961 } else {
962 /* Wait for UA to cancel */
963 fd->req.state = PLDM_FD_REQ_FAILED;
964 }
965 return 0;
966}
967
968LIBPLDM_CC_NONNULL
969static int pldm_fd_handle_apply_complete_resp(
970 struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
971 size_t resp_payload_len LIBPLDM_CC_UNUSED)
972{
973 if (fd->state != PLDM_FD_STATE_APPLY) {
974 return -EPROTO;
975 }
976
977 if (fd->req.state != PLDM_FD_REQ_SENT) {
978 /* Not waiting for a response, ignore it */
979 return -EPROTO;
980 }
981
982 assert(fd->req.complete);
983
984 /* Disregard the response completion code */
985
986 /* Next state depends whether the apply succeeded */
987 if (fd->req.result == PLDM_FWUP_APPLY_SUCCESS) {
988 /* Switch to ReadyXfer */
989 fd->req.state = PLDM_FD_REQ_UNUSED;
990 pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
991 } else {
992 /* Wait for UA to cancel */
993 fd->req.state = PLDM_FD_REQ_FAILED;
994 }
995 return 0;
996}
997
998LIBPLDM_CC_NONNULL
999static int pldm_fd_handle_resp(struct pldm_fd *fd, pldm_tid_t address,
1000 const void *resp_msg, size_t resp_len)
1001{
1002 size_t resp_payload_len;
1003 const struct pldm_msg *resp = resp_msg;
1004
1005 if (!(fd->ua_address_set && fd->ua_address == address)) {
1006 // Either an early response, or a response from a wrong TID */
1007 return -EBUSY;
1008 }
1009
1010 /* Must have a ccode */
1011 if (resp_len < sizeof(struct pldm_msg_hdr) + 1) {
1012 return -EINVAL;
1013 }
1014 resp_payload_len = resp_len - sizeof(struct pldm_msg_hdr);
1015
1016 if (fd->req.state != PLDM_FD_REQ_SENT) {
1017 // No response was expected
1018 return -EPROTO;
1019 }
1020
1021 if (fd->req.instance_id != resp->hdr.instance_id) {
1022 // Response wasn't for the expected request
1023 return -EPROTO;
1024 }
1025 if (fd->req.command != resp->hdr.command) {
1026 // Response wasn't for the expected request
1027 return -EPROTO;
1028 }
1029
1030 fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1031
1032 switch (resp->hdr.command) {
1033 case PLDM_REQUEST_FIRMWARE_DATA:
1034 return pldm_fd_handle_fwdata_resp(fd, resp, resp_payload_len);
1035 break;
1036 case PLDM_TRANSFER_COMPLETE:
1037 return pldm_fd_handle_transfer_complete_resp(fd, resp,
1038 resp_payload_len);
1039 break;
1040 case PLDM_VERIFY_COMPLETE:
1041 return pldm_fd_handle_verify_complete_resp(fd, resp,
1042 resp_payload_len);
1043 break;
1044 case PLDM_APPLY_COMPLETE:
1045 return pldm_fd_handle_apply_complete_resp(fd, resp,
1046 resp_payload_len);
1047 break;
1048 default:
1049 /* Unsolicited response. Already compared to command above */
1050 assert(false);
1051 return -EINVAL;
1052 }
1053}
1054
1055LIBPLDM_CC_NONNULL
1056static int pldm_fd_progress_download(struct pldm_fd *fd, struct pldm_msg *req,
1057 size_t *req_payload_len)
1058{
1059 uint8_t instance_id;
1060 struct pldm_fd_download *dl;
1061 int rc;
1062
1063 if (!pldm_fd_req_should_send(fd)) {
1064 /* Nothing to do */
1065 *req_payload_len = 0;
1066 return 0;
1067 }
1068
1069 instance_id = pldm_fd_req_next_instance(&fd->req);
1070 dl = &fd->specific.download;
1071 if (fd->req.complete) {
1072 /* Send TransferComplete */
1073 rc = encode_transfer_complete_req(instance_id, fd->req.result,
1074 req, req_payload_len);
1075 } else {
1076 /* Send a new RequestFirmwareData */
1077 const struct pldm_request_firmware_data_req req_params = {
1078 .offset = dl->offset,
1079 .length = pldm_fd_fwdata_size(fd),
1080 };
1081
1082 rc = encode_request_firmware_data_req(instance_id, &req_params,
1083 req, req_payload_len);
1084 }
1085
1086 if (rc) {
1087 return rc;
1088 }
1089
1090 /* Wait for response */
1091 fd->req.state = PLDM_FD_REQ_SENT;
1092 fd->req.instance_id = req->hdr.instance_id;
1093 fd->req.command = req->hdr.command;
1094 fd->req.sent_time = pldm_fd_now(fd);
1095
1096 return 0;
1097}
1098
1099LIBPLDM_CC_NONNULL
1100static int pldm_fd_progress_verify(struct pldm_fd *fd, struct pldm_msg *req,
1101 size_t *req_payload_len)
1102{
1103 uint8_t instance_id;
1104 int rc;
1105
1106 if (!pldm_fd_req_should_send(fd)) {
1107 /* Nothing to do */
1108 *req_payload_len = 0;
1109 return 0;
1110 }
1111
1112 if (!fd->req.complete) {
1113 bool pending = false;
1114 uint8_t res;
1115 res = fd->ops->verify(fd->ops_ctx, &fd->update_comp, &pending,
1116 &fd->specific.verify.progress_percent);
1117 if (pending) {
1118 if (res == PLDM_FWUP_VERIFY_SUCCESS) {
1119 /* Return without a VerifyComplete request.
1120 * Will call verify() again on next call */
1121 *req_payload_len = 0;
1122 return 0;
1123 }
1124 /* This is an API infraction by the implementer, return a distinctive failure */
1125 res = PLDM_FWUP_VENDOR_VERIFY_RESULT_RANGE_MAX;
1126 }
1127 fd->req.result = res;
1128 fd->req.complete = true;
1129 }
1130
1131 instance_id = pldm_fd_req_next_instance(&fd->req);
1132 rc = encode_verify_complete_req(instance_id, fd->req.result, req,
1133 req_payload_len);
1134 if (rc) {
1135 return rc;
1136 }
1137
1138 /* Wait for response */
1139 fd->req.state = PLDM_FD_REQ_SENT;
1140 fd->req.instance_id = req->hdr.instance_id;
1141 fd->req.command = req->hdr.command;
1142 fd->req.sent_time = pldm_fd_now(fd);
1143
1144 return 0;
1145}
1146
1147LIBPLDM_CC_NONNULL
1148static int pldm_fd_progress_apply(struct pldm_fd *fd, struct pldm_msg *req,
1149 size_t *req_payload_len)
1150{
1151 uint8_t instance_id;
1152 int rc;
1153
1154 if (!pldm_fd_req_should_send(fd)) {
1155 /* Nothing to do */
1156 *req_payload_len = 0;
1157 return 0;
1158 }
1159
1160 if (!fd->req.complete) {
1161 bool pending = false;
1162 uint8_t res;
1163 res = fd->ops->apply(fd->ops_ctx, &fd->update_comp, &pending,
1164 &fd->specific.apply.progress_percent);
1165 if (pending) {
1166 if (res == PLDM_FWUP_APPLY_SUCCESS) {
1167 /* Return without a ApplyComplete request.
1168 * Will call apply() again on next call */
1169 *req_payload_len = 0;
1170 return 0;
1171 }
1172 /* This is an API infraction by the implementer, return a distinctive failure */
1173 res = PLDM_FWUP_VENDOR_APPLY_RESULT_RANGE_MAX;
1174 }
1175 fd->req.result = res;
1176 fd->req.complete = true;
1177 if (fd->req.result ==
1178 PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD) {
1179 /* modified activation method isn't currently handled */
1180 fd->req.result = PLDM_FWUP_APPLY_SUCCESS;
1181 }
1182 }
1183
1184 instance_id = pldm_fd_req_next_instance(&fd->req);
1185 const struct pldm_apply_complete_req req_data = {
1186 .apply_result = fd->req.result,
1187 .comp_activation_methods_modification = { 0 },
1188 };
1189 rc = encode_apply_complete_req(instance_id, &req_data, req,
1190 req_payload_len);
1191 if (rc) {
1192 return rc;
1193 }
1194
1195 /* Wait for response */
1196 fd->req.state = PLDM_FD_REQ_SENT;
1197 fd->req.instance_id = req->hdr.instance_id;
1198 fd->req.command = req->hdr.command;
1199 fd->req.sent_time = pldm_fd_now(fd);
1200
1201 return 0;
1202}
1203
1204LIBPLDM_ABI_TESTING
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001205struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
1206 struct pldm_control *control)
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001207{
1208 struct pldm_fd *fd = malloc(sizeof(*fd));
1209 if (fd) {
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001210 if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx, control) ==
1211 0) {
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001212 return fd;
1213 }
1214 free(fd);
1215 fd = NULL;
1216 }
1217 return fd;
1218}
1219
1220LIBPLDM_ABI_TESTING
1221int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001222 const struct pldm_fd_ops *ops, void *ops_ctx,
1223 struct pldm_control *control)
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001224{
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001225 int rc;
1226
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001227 if (fd == NULL || ops == NULL) {
1228 return -EINVAL;
1229 }
1230
1231 if (ops->device_identifiers == NULL || ops->components == NULL ||
1232 ops->imageset_versions == NULL || ops->update_component == NULL ||
1233 ops->transfer_size == NULL || ops->firmware_data == NULL ||
1234 ops->verify == NULL || ops->activate == NULL ||
1235 ops->cancel_update_component == NULL || ops->now == NULL) {
1236 return -EINVAL;
1237 }
1238
1239 if (pldm_fd_size < sizeof(struct pldm_fd)) {
1240 /* Safety check that sufficient storage was provided for *fd,
1241 * in case PLDM_SIZEOF_PLDM_FD is incorrect */
1242 return -EINVAL;
1243 }
1244 memset(fd, 0x0, sizeof(*fd));
1245 fd->ops = ops;
1246 fd->ops_ctx = ops_ctx;
1247 fd->fd_t1_timeout = DEFAULT_FD_T1_TIMEOUT;
1248 fd->fd_t2_retry_time = DEFAULT_FD_T2_RETRY_TIME;
1249
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001250 if (control) {
1251 rc = pldm_control_add_type(control, PLDM_FWUP,
1252 &PLDM_FD_VERSIONS,
1253 PLDM_FD_VERSIONS_COUNT,
1254 PLDM_FD_COMMANDS);
1255 if (rc) {
1256 return rc;
1257 }
1258 }
1259
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001260 return 0;
1261}
1262
1263LIBPLDM_ABI_TESTING
1264int pldm_fd_handle_msg(struct pldm_fd *fd, pldm_tid_t remote_address,
1265 const void *in_msg, size_t in_len, void *out_msg,
1266 size_t *out_len)
1267{
1268 size_t req_payload_len;
1269 size_t resp_payload_len;
1270 struct pldm_header_info hdr;
1271 const struct pldm_msg *req = in_msg;
1272 struct pldm_msg *resp = out_msg;
1273 uint8_t rc;
1274
1275 if (fd == NULL || in_msg == NULL || out_msg == NULL ||
1276 out_len == NULL) {
1277 return -EINVAL;
1278 }
1279
1280 if (in_len < sizeof(struct pldm_msg_hdr)) {
1281 return -EOVERFLOW;
1282 }
1283 req_payload_len = in_len - sizeof(struct pldm_msg_hdr);
1284
1285 rc = unpack_pldm_header(&req->hdr, &hdr);
1286 if (rc != PLDM_SUCCESS) {
1287 return -EINVAL;
1288 }
1289
1290 if (hdr.pldm_type != PLDM_FWUP) {
1291 /* Caller should not have passed non-pldmfw */
1292 return -ENOMSG;
1293 }
1294
1295 if (hdr.msg_type == PLDM_RESPONSE) {
1296 *out_len = 0;
1297 return pldm_fd_handle_resp(fd, remote_address, in_msg, in_len);
1298 }
1299
1300 if (hdr.msg_type != PLDM_REQUEST) {
1301 return -EPROTO;
1302 }
1303
1304 /* Space for header plus completion code */
1305 if (*out_len < sizeof(struct pldm_msg_hdr) + 1) {
1306 return -EOVERFLOW;
1307 }
1308 resp_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1309
1310 /* Check address */
1311 switch (hdr.command) {
1312 /* Information or cancel commands are always allowed */
1313 case PLDM_QUERY_DEVICE_IDENTIFIERS:
1314 case PLDM_GET_FIRMWARE_PARAMETERS:
1315 case PLDM_GET_STATUS:
1316 case PLDM_CANCEL_UPDATE:
1317 case PLDM_QUERY_DOWNSTREAM_DEVICES:
1318 case PLDM_QUERY_DOWNSTREAM_IDENTIFIERS:
1319 case PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS:
1320 /* Request Update handler will set address */
1321 case PLDM_REQUEST_UPDATE:
1322 break;
1323 default:
1324 /* Requests must come from the same address that requested the update */
1325 if (!fd->ua_address_set || remote_address != fd->ua_address) {
1326 return pldm_fd_reply_cc(PLDM_ERROR_NOT_READY, &hdr,
1327 resp, &resp_payload_len);
1328 }
1329 }
1330
1331 /* Update timeout */
1332 switch (hdr.command) {
1333 case PLDM_REQUEST_UPDATE:
1334 case PLDM_PASS_COMPONENT_TABLE:
1335 case PLDM_UPDATE_COMPONENT:
1336 case PLDM_CANCEL_UPDATE:
1337 fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
1338 break;
1339 default:
1340 break;
1341 }
1342
Matt Johnstoncc6f6432024-11-27 10:02:15 +08001343 /* Dispatch command.
1344 Update PLDM_FD_COMMANDS if adding new handlers */
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001345 switch (hdr.command) {
1346 case PLDM_QUERY_DEVICE_IDENTIFIERS:
1347 rc = pldm_fd_qdi(fd, &hdr, req, req_payload_len, resp,
1348 &resp_payload_len);
1349 break;
1350 case PLDM_GET_FIRMWARE_PARAMETERS:
1351 rc = pldm_fd_fw_param(fd, &hdr, req, req_payload_len, resp,
1352 &resp_payload_len);
1353 break;
1354 case PLDM_REQUEST_UPDATE:
1355 rc = pldm_fd_request_update(fd, &hdr, req, req_payload_len,
1356 resp, &resp_payload_len,
1357 remote_address);
1358 break;
1359 case PLDM_PASS_COMPONENT_TABLE:
1360 rc = pldm_fd_pass_comp(fd, &hdr, req, req_payload_len, resp,
1361 &resp_payload_len);
1362 break;
1363 case PLDM_UPDATE_COMPONENT:
1364 rc = pldm_fd_update_comp(fd, &hdr, req, req_payload_len, resp,
1365 &resp_payload_len);
1366 break;
1367 case PLDM_GET_STATUS:
1368 rc = pldm_fd_get_status(fd, &hdr, req, req_payload_len, resp,
1369 &resp_payload_len);
1370 break;
1371 case PLDM_CANCEL_UPDATE_COMPONENT:
1372 rc = pldm_fd_cancel_update_comp(fd, &hdr, req, req_payload_len,
1373 resp, &resp_payload_len);
1374 break;
1375 case PLDM_CANCEL_UPDATE:
1376 rc = pldm_fd_cancel_update(fd, &hdr, req, req_payload_len, resp,
1377 &resp_payload_len);
1378 break;
1379 case PLDM_ACTIVATE_FIRMWARE:
1380 rc = pldm_fd_activate_firmware(fd, &hdr, req, req_payload_len,
1381 resp, &resp_payload_len);
1382 break;
1383 default:
1384 rc = pldm_fd_reply_cc(PLDM_ERROR_UNSUPPORTED_PLDM_CMD, &hdr,
1385 resp, &resp_payload_len);
1386 }
1387
1388 if (rc == 0) {
1389 *out_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
1390 }
1391
1392 return rc;
1393}
1394
1395LIBPLDM_ABI_TESTING
1396int pldm_fd_progress(struct pldm_fd *fd, void *out_msg, size_t *out_len,
1397 pldm_tid_t *address)
1398{
1399 size_t req_payload_len;
1400 struct pldm_msg *req = out_msg;
1401 int rc = -EINVAL;
Matt Johnston26289902025-02-13 10:11:47 +08001402 bool ua_timeout_check = false;
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001403
1404 if (fd == NULL || out_msg == NULL || out_len == NULL) {
1405 return -EINVAL;
1406 }
1407
1408 /* Space for header */
1409 if (*out_len < sizeof(struct pldm_msg_hdr)) {
1410 return -EOVERFLOW;
1411 }
1412 req_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1413 *out_len = 0;
1414
Matt Johnston26289902025-02-13 10:11:47 +08001415 // Handle FD-driven states
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001416 switch (fd->state) {
1417 case PLDM_FD_STATE_DOWNLOAD:
1418 rc = pldm_fd_progress_download(fd, req, &req_payload_len);
1419 break;
1420 case PLDM_FD_STATE_VERIFY:
1421 rc = pldm_fd_progress_verify(fd, req, &req_payload_len);
1422 break;
1423 case PLDM_FD_STATE_APPLY:
1424 rc = pldm_fd_progress_apply(fd, req, &req_payload_len);
1425 break;
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001426 default:
1427 req_payload_len = 0;
Matt Johnston26289902025-02-13 10:11:47 +08001428 break;
1429 }
1430
1431 // Cancel update if expected UA message isn't received within timeout
1432 switch (fd->state) {
1433 case PLDM_FD_STATE_DOWNLOAD:
1434 case PLDM_FD_STATE_VERIFY:
1435 case PLDM_FD_STATE_APPLY:
1436 // FD-driven states will time out if a response isn't received
1437 ua_timeout_check = (fd->req.state == PLDM_FD_REQ_SENT);
1438 break;
1439 case PLDM_FD_STATE_IDLE:
1440 ua_timeout_check = false;
1441 break;
1442 default:
1443 // Other Update Mode states have a timeout for the UA to
1444 // send a request
1445 ua_timeout_check = true;
1446 }
1447
1448 if (ua_timeout_check) {
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001449 if ((pldm_fd_now(fd) - fd->update_timestamp_fd_t1) >
1450 fd->fd_t1_timeout) {
1451 pldm_fd_maybe_cancel_component(fd);
1452 pldm_fd_idle_timeout(fd);
Matt Johnston26289902025-02-13 10:11:47 +08001453 req_payload_len = 0;
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001454 }
Matt Johnston8c2bfb12024-11-07 15:31:32 +08001455 }
1456
1457 if (rc == 0 && fd->ua_address_set && req_payload_len > 0) {
1458 *out_len = req_payload_len + sizeof(struct pldm_msg_hdr);
1459 *address = fd->ua_address;
1460 }
1461
1462 return rc;
1463}
1464
1465LIBPLDM_ABI_TESTING
1466int pldm_fd_set_update_idle_timeout(struct pldm_fd *fd, uint32_t time)
1467{
1468 if (fd == NULL) {
1469 return -EINVAL;
1470 }
1471
1472 fd->fd_t1_timeout = time;
1473 return 0;
1474}
1475
1476LIBPLDM_ABI_TESTING
1477int pldm_fd_set_request_retry_time(struct pldm_fd *fd, uint32_t time)
1478{
1479 if (fd == NULL) {
1480 return -EINVAL;
1481 }
1482
1483 fd->fd_t2_retry_time = time;
1484 return 0;
1485}