1 /* 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])*/
17 static const pldm_fd_time_t DEFAULT_FD_T1_TIMEOUT = 120000;
18
19 /* FD_T2 "Retry request for firmware data", 1 second (range [1s, 5s]) */
20 static const pldm_fd_time_t DEFAULT_FD_T2_RETRY_TIME = 1000;
21
22 static const uint8_t INSTANCE_ID_COUNT = 32;
23 static const uint8_t PROGRESS_PERCENT_NOT_SUPPORTED = 101;
24
25 #define PLDM_FD_VERSIONS_COUNT 2
26 static 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 };
34 const 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
52 /* Ensure that public definition is kept updated */
53 static_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. */
59 LIBPLDM_CC_NONNULL
pldm_fd_reply_cc(uint8_t ccode,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)60 static 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. */
82 LIBPLDM_CC_NONNULL
pldm_fd_reply_errno(int err,const struct pldm_header_info * req_hdr,struct pldm_msg * resp,size_t * resp_payload_len)83 static 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
111 LIBPLDM_CC_NONNULL
pldm_fd_set_state(struct pldm_fd * fd,enum pldm_firmware_device_states state)112 static 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
126 LIBPLDM_CC_NONNULL
pldm_fd_set_idle(struct pldm_fd * fd,enum pldm_get_status_reason_code_values reason)127 static 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
136 LIBPLDM_CC_NONNULL
pldm_fd_idle_timeout(struct pldm_fd * fd)137 static 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
168 LIBPLDM_CC_NONNULL
pldm_fd_get_aux_state(const struct pldm_fd * fd,uint8_t * aux_state,uint8_t * aux_state_status)169 static 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
195 LIBPLDM_CC_NONNULL
pldm_fd_now(struct pldm_fd * fd)196 static uint64_t pldm_fd_now(struct pldm_fd *fd)
197 {
198 return fd->ops->now(fd->ops_ctx);
199 }
200
201 LIBPLDM_CC_NONNULL
pldm_fd_req_should_send(struct pldm_fd * fd)202 static 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 */
228 LIBPLDM_CC_NONNULL
pldm_fd_req_next_instance(struct pldm_fd_req * req)229 static 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
235 LIBPLDM_CC_NONNULL
pldm_fd_qdi(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)236 static 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
270 LIBPLDM_CC_NONNULL
pldm_fd_fw_param(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)271 static 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
370 LIBPLDM_CC_NONNULL
pldm_fd_request_update(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len,uint8_t address)371 static 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 */
423 LIBPLDM_CC_NONNULL
pldm_fd_maybe_cancel_component(struct pldm_fd * fd)424 static 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() */
454 LIBPLDM_CC_NONNULL
pldm_fd_check_update_component(struct pldm_fd * fd,bool update,const struct pldm_firmware_update_component * comp)455 static 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
486 LIBPLDM_CC_NONNULL
pldm_fd_pass_comp(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)487 static 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
540 LIBPLDM_CC_NONNULL
pldm_fd_update_comp(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)541 static 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
613 LIBPLDM_CC_NONNULL
pldm_fd_get_status(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)614 static 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
673 LIBPLDM_CC_NONNULL
pldm_fd_cancel_update_comp(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)674 static 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
709 LIBPLDM_CC_NONNULL
pldm_fd_cancel_update(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req LIBPLDM_CC_UNUSED,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)710 static 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
747 LIBPLDM_CC_NONNULL
pldm_fd_activate_firmware(struct pldm_fd * fd,const struct pldm_header_info * hdr,const struct pldm_msg * req,size_t req_payload_len,struct pldm_msg * resp,size_t * resp_payload_len)748 static 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
798 LIBPLDM_CC_NONNULL
pldm_fd_fwdata_size(struct pldm_fd * fd)799 static 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
820 LIBPLDM_CC_NONNULL
pldm_fd_handle_fwdata_resp(struct pldm_fd * fd,const struct pldm_msg * resp,size_t resp_payload_len)821 static 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
896 LIBPLDM_CC_NONNULL
pldm_fd_handle_transfer_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)897 static 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
933 LIBPLDM_CC_NONNULL
pldm_fd_handle_verify_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)934 static 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
967 LIBPLDM_CC_NONNULL
pldm_fd_handle_apply_complete_resp(struct pldm_fd * fd,const struct pldm_msg * resp LIBPLDM_CC_UNUSED,size_t resp_payload_len LIBPLDM_CC_UNUSED)968 static 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
997 LIBPLDM_CC_NONNULL
pldm_fd_handle_resp(struct pldm_fd * fd,pldm_tid_t address,const void * resp_msg,size_t resp_len)998 static 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
1054 LIBPLDM_CC_NONNULL
pldm_fd_progress_download(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1055 static 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
1098 LIBPLDM_CC_NONNULL
pldm_fd_progress_verify(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1099 static 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
1146 LIBPLDM_CC_NONNULL
pldm_fd_progress_apply(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)1147 static 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
1203 LIBPLDM_ABI_TESTING
pldm_fd_new(const struct pldm_fd_ops * ops,void * ops_ctx,struct pldm_control * control)1204 struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
1205 struct pldm_control *control)
1206 {
1207 struct pldm_fd *fd = malloc(sizeof(*fd));
1208 if (fd) {
1209 if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx, control) ==
1210 0) {
1211 return fd;
1212 }
1213 free(fd);
1214 fd = NULL;
1215 }
1216 return fd;
1217 }
1218
1219 LIBPLDM_ABI_TESTING
pldm_fd_setup(struct pldm_fd * fd,size_t pldm_fd_size,const struct pldm_fd_ops * ops,void * ops_ctx,struct pldm_control * control)1220 int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
1221 const struct pldm_fd_ops *ops, void *ops_ctx,
1222 struct pldm_control *control)
1223 {
1224 int rc;
1225
1226 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
1249 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
1259 return 0;
1260 }
1261
1262 LIBPLDM_ABI_TESTING
pldm_fd_handle_msg(struct pldm_fd * fd,pldm_tid_t remote_address,const void * in_msg,size_t in_len,void * out_msg,size_t * out_len)1263 int 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
1342 /* Dispatch command.
1343 Update PLDM_FD_COMMANDS if adding new handlers */
1344 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
1394 LIBPLDM_ABI_TESTING
pldm_fd_progress(struct pldm_fd * fd,void * out_msg,size_t * out_len,pldm_tid_t * address)1395 int 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 bool ua_timeout_check = false;
1402
1403 if (fd == NULL || out_msg == NULL || out_len == NULL) {
1404 return -EINVAL;
1405 }
1406
1407 /* Space for header */
1408 if (*out_len < sizeof(struct pldm_msg_hdr)) {
1409 return -EOVERFLOW;
1410 }
1411 req_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
1412 *out_len = 0;
1413
1414 // Handle FD-driven states
1415 switch (fd->state) {
1416 case PLDM_FD_STATE_DOWNLOAD:
1417 rc = pldm_fd_progress_download(fd, req, &req_payload_len);
1418 break;
1419 case PLDM_FD_STATE_VERIFY:
1420 rc = pldm_fd_progress_verify(fd, req, &req_payload_len);
1421 break;
1422 case PLDM_FD_STATE_APPLY:
1423 rc = pldm_fd_progress_apply(fd, req, &req_payload_len);
1424 break;
1425 default:
1426 req_payload_len = 0;
1427 break;
1428 }
1429
1430 // Cancel update if expected UA message isn't received within timeout
1431 switch (fd->state) {
1432 case PLDM_FD_STATE_DOWNLOAD:
1433 case PLDM_FD_STATE_VERIFY:
1434 case PLDM_FD_STATE_APPLY:
1435 // FD-driven states will time out if a response isn't received
1436 ua_timeout_check = (fd->req.state == PLDM_FD_REQ_SENT);
1437 break;
1438 case PLDM_FD_STATE_IDLE:
1439 ua_timeout_check = false;
1440 break;
1441 default:
1442 // Other Update Mode states have a timeout for the UA to
1443 // send a request
1444 ua_timeout_check = true;
1445 }
1446
1447 if (ua_timeout_check) {
1448 if ((pldm_fd_now(fd) - fd->update_timestamp_fd_t1) >
1449 fd->fd_t1_timeout) {
1450 pldm_fd_maybe_cancel_component(fd);
1451 pldm_fd_idle_timeout(fd);
1452 req_payload_len = 0;
1453 }
1454 }
1455
1456 if (rc == 0 && fd->ua_address_set && req_payload_len > 0) {
1457 *out_len = req_payload_len + sizeof(struct pldm_msg_hdr);
1458 *address = fd->ua_address;
1459 }
1460
1461 return rc;
1462 }
1463
1464 LIBPLDM_ABI_TESTING
pldm_fd_set_update_idle_timeout(struct pldm_fd * fd,uint32_t time)1465 int pldm_fd_set_update_idle_timeout(struct pldm_fd *fd, uint32_t time)
1466 {
1467 if (fd == NULL) {
1468 return -EINVAL;
1469 }
1470
1471 fd->fd_t1_timeout = time;
1472 return 0;
1473 }
1474
1475 LIBPLDM_ABI_TESTING
pldm_fd_set_request_retry_time(struct pldm_fd * fd,uint32_t time)1476 int pldm_fd_set_request_retry_time(struct pldm_fd *fd, uint32_t time)
1477 {
1478 if (fd == NULL) {
1479 return -EINVAL;
1480 }
1481
1482 fd->fd_t2_retry_time = time;
1483 return 0;
1484 }
1485