18c2bfb14SMatt Johnston /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
28c2bfb14SMatt Johnston #include <stdint.h>
38c2bfb14SMatt Johnston #include <stdbool.h>
48c2bfb14SMatt Johnston #include <string.h>
58c2bfb14SMatt Johnston #include <stdlib.h>
68c2bfb14SMatt Johnston
78c2bfb14SMatt Johnston #include <libpldm/pldm.h>
88c2bfb14SMatt Johnston #include <libpldm/firmware_update.h>
98c2bfb14SMatt Johnston #include <libpldm/firmware_fd.h>
108c2bfb14SMatt Johnston #include <libpldm/utils.h>
118c2bfb14SMatt Johnston #include <compiler.h>
128c2bfb14SMatt Johnston #include <msgbuf.h>
138c2bfb14SMatt Johnston
148c2bfb14SMatt Johnston #include "fd-internal.h"
158c2bfb14SMatt Johnston
168c2bfb14SMatt Johnston /* FD_T1 Update mode idle timeout, 120 seconds (range [60s, 120s])*/
178c2bfb14SMatt Johnston static const pldm_fd_time_t DEFAULT_FD_T1_TIMEOUT = 120000;
188c2bfb14SMatt Johnston
198c2bfb14SMatt Johnston /* FD_T2 "Retry request for firmware data", 1 second (range [1s, 5s]) */
208c2bfb14SMatt Johnston static const pldm_fd_time_t DEFAULT_FD_T2_RETRY_TIME = 1000;
218c2bfb14SMatt Johnston
228c2bfb14SMatt Johnston static const uint8_t INSTANCE_ID_COUNT = 32;
238c2bfb14SMatt Johnston static const uint8_t PROGRESS_PERCENT_NOT_SUPPORTED = 101;
248c2bfb14SMatt Johnston
25cc6f6434SMatt Johnston #define PLDM_FD_VERSIONS_COUNT 2
26cc6f6434SMatt Johnston static const uint32_t PLDM_FD_VERSIONS[PLDM_FD_VERSIONS_COUNT] = {
27cc6f6434SMatt Johnston /* Only PLDM Firmware 1.1.0 is current implemented. */
28cc6f6434SMatt Johnston 0xf1f1f000,
29cc6f6434SMatt Johnston /* CRC. Calculated with python:
30cc6f6434SMatt Johnston hex(crccheck.crc.Crc32.calc(struct.pack('<I', 0xf1f1f000)))
31cc6f6434SMatt Johnston */
32cc6f6434SMatt Johnston 0x539dbeba,
33cc6f6434SMatt Johnston };
34cc6f6434SMatt Johnston const bitfield8_t PLDM_FD_COMMANDS[32] = {
35cc6f6434SMatt Johnston // 0x00..0x07
36cc6f6434SMatt Johnston { .byte = (1 << PLDM_QUERY_DEVICE_IDENTIFIERS |
37cc6f6434SMatt Johnston 1 << PLDM_GET_FIRMWARE_PARAMETERS) },
38cc6f6434SMatt Johnston { 0 },
39cc6f6434SMatt Johnston // 0x10..0x17
40cc6f6434SMatt Johnston { .byte = (1u << PLDM_REQUEST_UPDATE | 1u << PLDM_PASS_COMPONENT_TABLE |
41cc6f6434SMatt Johnston 1u << PLDM_UPDATE_COMPONENT) >>
42cc6f6434SMatt Johnston 0x10 },
43cc6f6434SMatt Johnston // 0x18..0x1f
44cc6f6434SMatt Johnston {
45cc6f6434SMatt Johnston .byte = (1u << PLDM_ACTIVATE_FIRMWARE | 1u << PLDM_GET_STATUS |
46cc6f6434SMatt Johnston 1u << PLDM_CANCEL_UPDATE_COMPONENT |
47cc6f6434SMatt Johnston 1u << PLDM_CANCEL_UPDATE) >>
48cc6f6434SMatt Johnston 0x18,
49cc6f6434SMatt Johnston },
50cc6f6434SMatt Johnston };
51cc6f6434SMatt Johnston
528c2bfb14SMatt Johnston /* Ensure that public definition is kept updated */
538c2bfb14SMatt Johnston static_assert(alignof(struct pldm_fd) == PLDM_ALIGNOF_PLDM_FD,
548c2bfb14SMatt Johnston "PLDM_ALIGNOF_PLDM_FD wrong");
558c2bfb14SMatt Johnston
568c2bfb14SMatt Johnston /* Maybe called with success or failure completion codes, though
578c2bfb14SMatt Johnston * success only makes sense for responses without a body.
588c2bfb14SMatt Johnston * Returns 0 or negative errno. */
598c2bfb14SMatt Johnston 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)608c2bfb14SMatt Johnston static int pldm_fd_reply_cc(uint8_t ccode,
618c2bfb14SMatt Johnston const struct pldm_header_info *req_hdr,
628c2bfb14SMatt Johnston struct pldm_msg *resp, size_t *resp_payload_len)
638c2bfb14SMatt Johnston {
648c2bfb14SMatt Johnston int status;
658c2bfb14SMatt Johnston
668c2bfb14SMatt Johnston /* 1 byte completion code */
678c2bfb14SMatt Johnston if (*resp_payload_len < 1) {
688c2bfb14SMatt Johnston return -EOVERFLOW;
698c2bfb14SMatt Johnston }
708c2bfb14SMatt Johnston *resp_payload_len = 1;
718c2bfb14SMatt Johnston
728c2bfb14SMatt Johnston status = encode_cc_only_resp(req_hdr->instance, PLDM_FWUP,
738c2bfb14SMatt Johnston req_hdr->command, ccode, resp);
748c2bfb14SMatt Johnston if (status != PLDM_SUCCESS) {
758c2bfb14SMatt Johnston return -EINVAL;
768c2bfb14SMatt Johnston }
778c2bfb14SMatt Johnston return 0;
788c2bfb14SMatt Johnston }
798c2bfb14SMatt Johnston
808c2bfb14SMatt Johnston /* Must be called with a negative errno.
818c2bfb14SMatt Johnston * Returns 0 or negative errno. */
828c2bfb14SMatt Johnston 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)838c2bfb14SMatt Johnston static int pldm_fd_reply_errno(int err, const struct pldm_header_info *req_hdr,
848c2bfb14SMatt Johnston struct pldm_msg *resp, size_t *resp_payload_len)
858c2bfb14SMatt Johnston {
868c2bfb14SMatt Johnston uint8_t ccode = PLDM_ERROR;
878c2bfb14SMatt Johnston
888c2bfb14SMatt Johnston assert(err < 0);
898c2bfb14SMatt Johnston switch (err) {
908c2bfb14SMatt Johnston case -EINVAL:
918c2bfb14SMatt Johnston // internal error, shouldn't occur.
928c2bfb14SMatt Johnston ccode = PLDM_ERROR;
938c2bfb14SMatt Johnston break;
948c2bfb14SMatt Johnston case -EPROTO:
958c2bfb14SMatt Johnston // Bad data from peer
968c2bfb14SMatt Johnston ccode = PLDM_ERROR_INVALID_DATA;
978c2bfb14SMatt Johnston break;
988c2bfb14SMatt Johnston case -EOVERFLOW:
998c2bfb14SMatt Johnston case -EBADMSG:
1008c2bfb14SMatt Johnston // Bad data from peer
1018c2bfb14SMatt Johnston ccode = PLDM_ERROR_INVALID_LENGTH;
1028c2bfb14SMatt Johnston break;
1038c2bfb14SMatt Johnston default:
1048c2bfb14SMatt Johnston // general error
1058c2bfb14SMatt Johnston ccode = PLDM_ERROR;
1068c2bfb14SMatt Johnston }
1078c2bfb14SMatt Johnston
1088c2bfb14SMatt Johnston return pldm_fd_reply_cc(ccode, req_hdr, resp, resp_payload_len);
1098c2bfb14SMatt Johnston }
1108c2bfb14SMatt Johnston
1118c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_set_state(struct pldm_fd * fd,enum pldm_firmware_device_states state)1128c2bfb14SMatt Johnston static void pldm_fd_set_state(struct pldm_fd *fd,
1138c2bfb14SMatt Johnston enum pldm_firmware_device_states state)
1148c2bfb14SMatt Johnston {
1158c2bfb14SMatt Johnston /* pldm_fd_set_idle should be used instead */
1168c2bfb14SMatt Johnston assert(state != PLDM_FD_STATE_IDLE);
1178c2bfb14SMatt Johnston
1188c2bfb14SMatt Johnston if (fd->state == state) {
1198c2bfb14SMatt Johnston return;
1208c2bfb14SMatt Johnston }
1218c2bfb14SMatt Johnston
1228c2bfb14SMatt Johnston fd->prev_state = fd->state;
1238c2bfb14SMatt Johnston fd->state = state;
1248c2bfb14SMatt Johnston }
1258c2bfb14SMatt Johnston
1268c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_set_idle(struct pldm_fd * fd,enum pldm_get_status_reason_code_values reason)1278c2bfb14SMatt Johnston static void pldm_fd_set_idle(struct pldm_fd *fd,
1288c2bfb14SMatt Johnston enum pldm_get_status_reason_code_values reason)
1298c2bfb14SMatt Johnston {
1308c2bfb14SMatt Johnston fd->prev_state = fd->state;
1318c2bfb14SMatt Johnston fd->state = PLDM_FD_STATE_IDLE;
1328c2bfb14SMatt Johnston fd->reason = reason;
1338c2bfb14SMatt Johnston fd->ua_address_set = false;
1348c2bfb14SMatt Johnston }
1358c2bfb14SMatt Johnston
1368c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_idle_timeout(struct pldm_fd * fd)1378c2bfb14SMatt Johnston static void pldm_fd_idle_timeout(struct pldm_fd *fd)
1388c2bfb14SMatt Johnston {
1398c2bfb14SMatt Johnston enum pldm_get_status_reason_code_values reason = PLDM_FD_INITIALIZATION;
1408c2bfb14SMatt Johnston
1418c2bfb14SMatt Johnston switch (fd->state) {
1428c2bfb14SMatt Johnston case PLDM_FD_STATE_IDLE:
1438c2bfb14SMatt Johnston return;
1448c2bfb14SMatt Johnston case PLDM_FD_STATE_LEARN_COMPONENTS:
1458c2bfb14SMatt Johnston reason = PLDM_FD_TIMEOUT_LEARN_COMPONENT;
1468c2bfb14SMatt Johnston break;
1478c2bfb14SMatt Johnston case PLDM_FD_STATE_READY_XFER:
1488c2bfb14SMatt Johnston reason = PLDM_FD_TIMEOUT_READY_XFER;
1498c2bfb14SMatt Johnston break;
1508c2bfb14SMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
1518c2bfb14SMatt Johnston reason = PLDM_FD_TIMEOUT_DOWNLOAD;
1528c2bfb14SMatt Johnston break;
1538c2bfb14SMatt Johnston case PLDM_FD_STATE_VERIFY:
1548c2bfb14SMatt Johnston reason = PLDM_FD_TIMEOUT_VERIFY;
1558c2bfb14SMatt Johnston break;
1568c2bfb14SMatt Johnston case PLDM_FD_STATE_APPLY:
1578c2bfb14SMatt Johnston reason = PLDM_FD_TIMEOUT_APPLY;
1588c2bfb14SMatt Johnston break;
1598c2bfb14SMatt Johnston case PLDM_FD_STATE_ACTIVATE:
1608c2bfb14SMatt Johnston /* Not a timeout */
1618c2bfb14SMatt Johnston reason = PLDM_FD_ACTIVATE_FW;
1628c2bfb14SMatt Johnston break;
1638c2bfb14SMatt Johnston }
1648c2bfb14SMatt Johnston
1658c2bfb14SMatt Johnston pldm_fd_set_idle(fd, reason);
1668c2bfb14SMatt Johnston }
1678c2bfb14SMatt Johnston
1688c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_get_aux_state(const struct pldm_fd * fd,uint8_t * aux_state,uint8_t * aux_state_status)1698c2bfb14SMatt Johnston static void pldm_fd_get_aux_state(const struct pldm_fd *fd, uint8_t *aux_state,
1708c2bfb14SMatt Johnston uint8_t *aux_state_status)
1718c2bfb14SMatt Johnston {
1728c2bfb14SMatt Johnston *aux_state_status = 0;
1738c2bfb14SMatt Johnston
1748c2bfb14SMatt Johnston switch (fd->req.state) {
1758c2bfb14SMatt Johnston case PLDM_FD_REQ_UNUSED:
1768c2bfb14SMatt Johnston *aux_state = PLDM_FD_IDLE_LEARN_COMPONENTS_READ_XFER;
1778c2bfb14SMatt Johnston break;
1788c2bfb14SMatt Johnston case PLDM_FD_REQ_SENT:
1798c2bfb14SMatt Johnston *aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
1808c2bfb14SMatt Johnston break;
1818c2bfb14SMatt Johnston case PLDM_FD_REQ_READY:
1828c2bfb14SMatt Johnston if (fd->req.complete) {
1838c2bfb14SMatt Johnston *aux_state = PLDM_FD_OPERATION_SUCCESSFUL;
1848c2bfb14SMatt Johnston } else {
1858c2bfb14SMatt Johnston *aux_state = PLDM_FD_OPERATION_IN_PROGRESS;
1868c2bfb14SMatt Johnston }
1878c2bfb14SMatt Johnston break;
1888c2bfb14SMatt Johnston case PLDM_FD_REQ_FAILED:
1898c2bfb14SMatt Johnston *aux_state = PLDM_FD_OPERATION_FAILED;
1908c2bfb14SMatt Johnston *aux_state_status = fd->req.result;
1918c2bfb14SMatt Johnston break;
1928c2bfb14SMatt Johnston }
1938c2bfb14SMatt Johnston }
1948c2bfb14SMatt Johnston
1958c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_now(struct pldm_fd * fd)1968c2bfb14SMatt Johnston static uint64_t pldm_fd_now(struct pldm_fd *fd)
1978c2bfb14SMatt Johnston {
1988c2bfb14SMatt Johnston return fd->ops->now(fd->ops_ctx);
1998c2bfb14SMatt Johnston }
2008c2bfb14SMatt Johnston
2018c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_req_should_send(struct pldm_fd * fd)2028c2bfb14SMatt Johnston static bool pldm_fd_req_should_send(struct pldm_fd *fd)
2038c2bfb14SMatt Johnston {
2048c2bfb14SMatt Johnston pldm_fd_time_t now = pldm_fd_now(fd);
2058c2bfb14SMatt Johnston
2068c2bfb14SMatt Johnston switch (fd->req.state) {
2078c2bfb14SMatt Johnston case PLDM_FD_REQ_UNUSED:
2088c2bfb14SMatt Johnston assert(false);
2098c2bfb14SMatt Johnston return false;
2108c2bfb14SMatt Johnston case PLDM_FD_REQ_READY:
2118c2bfb14SMatt Johnston return true;
2128c2bfb14SMatt Johnston case PLDM_FD_REQ_FAILED:
2138c2bfb14SMatt Johnston return false;
2148c2bfb14SMatt Johnston case PLDM_FD_REQ_SENT:
2158c2bfb14SMatt Johnston if (now < fd->req.sent_time) {
2168c2bfb14SMatt Johnston /* Time went backwards */
2178c2bfb14SMatt Johnston return false;
2188c2bfb14SMatt Johnston }
2198c2bfb14SMatt Johnston
2208c2bfb14SMatt Johnston /* Send if retry time has elapsed */
2218c2bfb14SMatt Johnston return (now - fd->req.sent_time) >= fd->fd_t2_retry_time;
2228c2bfb14SMatt Johnston }
2238c2bfb14SMatt Johnston return false;
2248c2bfb14SMatt Johnston }
2258c2bfb14SMatt Johnston
2268c2bfb14SMatt Johnston /* Allocate the next instance ID. Only one request is outstanding so cycling
2278c2bfb14SMatt Johnston * through the range is OK */
2288c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_req_next_instance(struct pldm_fd_req * req)2298c2bfb14SMatt Johnston static uint8_t pldm_fd_req_next_instance(struct pldm_fd_req *req)
2308c2bfb14SMatt Johnston {
2318c2bfb14SMatt Johnston req->instance_id = (req->instance_id + 1) % INSTANCE_ID_COUNT;
2328c2bfb14SMatt Johnston return req->instance_id;
2338c2bfb14SMatt Johnston }
2348c2bfb14SMatt Johnston
2358c2bfb14SMatt Johnston 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)2368c2bfb14SMatt Johnston static int pldm_fd_qdi(struct pldm_fd *fd, const struct pldm_header_info *hdr,
2378c2bfb14SMatt Johnston const struct pldm_msg *req LIBPLDM_CC_UNUSED,
2388c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
2398c2bfb14SMatt Johnston size_t *resp_payload_len)
2408c2bfb14SMatt Johnston {
2418c2bfb14SMatt Johnston uint8_t descriptor_count;
2428c2bfb14SMatt Johnston const struct pldm_descriptor *descriptors;
2438c2bfb14SMatt Johnston int rc;
2448c2bfb14SMatt Johnston
2458c2bfb14SMatt Johnston /* QDI has no request data */
2468c2bfb14SMatt Johnston if (req_payload_len != PLDM_QUERY_DEVICE_IDENTIFIERS_REQ_BYTES) {
2478c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
2488c2bfb14SMatt Johnston resp_payload_len);
2498c2bfb14SMatt Johnston }
2508c2bfb14SMatt Johnston
2518c2bfb14SMatt Johnston /* Retrieve platform-specific data */
2528c2bfb14SMatt Johnston rc = fd->ops->device_identifiers(fd->ops_ctx, &descriptor_count,
2538c2bfb14SMatt Johnston &descriptors);
2548c2bfb14SMatt Johnston if (rc) {
2558c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
2568c2bfb14SMatt Johnston resp_payload_len);
2578c2bfb14SMatt Johnston }
2588c2bfb14SMatt Johnston
2598c2bfb14SMatt Johnston rc = encode_query_device_identifiers_resp(hdr->instance,
2608c2bfb14SMatt Johnston descriptor_count, descriptors,
2618c2bfb14SMatt Johnston resp, resp_payload_len);
2628c2bfb14SMatt Johnston
2638c2bfb14SMatt Johnston if (rc) {
2648c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
2658c2bfb14SMatt Johnston }
2668c2bfb14SMatt Johnston
2678c2bfb14SMatt Johnston return 0;
2688c2bfb14SMatt Johnston }
2698c2bfb14SMatt Johnston
2708c2bfb14SMatt Johnston 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)2718c2bfb14SMatt Johnston static int pldm_fd_fw_param(struct pldm_fd *fd,
2728c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
2738c2bfb14SMatt Johnston const struct pldm_msg *req LIBPLDM_CC_UNUSED,
2748c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
2758c2bfb14SMatt Johnston size_t *resp_payload_len)
2768c2bfb14SMatt Johnston {
2778c2bfb14SMatt Johnston uint16_t entry_count;
2788c2bfb14SMatt Johnston const struct pldm_firmware_component_standalone **entries;
2798c2bfb14SMatt Johnston struct pldm_msgbuf _buf;
2808c2bfb14SMatt Johnston struct pldm_msgbuf *buf = &_buf;
2818c2bfb14SMatt Johnston int rc;
2828c2bfb14SMatt Johnston
2838c2bfb14SMatt Johnston /* No request data */
2848c2bfb14SMatt Johnston if (req_payload_len != PLDM_GET_FIRMWARE_PARAMETERS_REQ_BYTES) {
2858c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
2868c2bfb14SMatt Johnston resp_payload_len);
2878c2bfb14SMatt Johnston }
2888c2bfb14SMatt Johnston
2898c2bfb14SMatt Johnston /* Retrieve platform-specific data */
2908c2bfb14SMatt Johnston rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
2918c2bfb14SMatt Johnston if (rc) {
2928c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
2938c2bfb14SMatt Johnston resp_payload_len);
2948c2bfb14SMatt Johnston }
2958c2bfb14SMatt Johnston
2968c2bfb14SMatt Johnston rc = pldm_msgbuf_init_errno(buf, 0, resp->payload, *resp_payload_len);
2978c2bfb14SMatt Johnston if (rc) {
2988c2bfb14SMatt Johnston return rc;
2998c2bfb14SMatt Johnston }
3008c2bfb14SMatt Johnston
3018c2bfb14SMatt Johnston /* Add the fixed parameters */
3028c2bfb14SMatt Johnston {
3038c2bfb14SMatt Johnston struct pldm_get_firmware_parameters_resp_full fwp = {
3048c2bfb14SMatt Johnston .completion_code = PLDM_SUCCESS,
3058c2bfb14SMatt Johnston // TODO defaulted to 0, could have a callback.
3068c2bfb14SMatt Johnston .capabilities_during_update = { 0 },
3078c2bfb14SMatt Johnston .comp_count = entry_count,
3088c2bfb14SMatt Johnston };
3098c2bfb14SMatt Johnston /* fill active and pending strings */
3108c2bfb14SMatt Johnston rc = fd->ops->imageset_versions(
3118c2bfb14SMatt Johnston fd->ops_ctx, &fwp.active_comp_image_set_ver_str,
3128c2bfb14SMatt Johnston &fwp.pending_comp_image_set_ver_str);
3138c2bfb14SMatt Johnston if (rc) {
3148c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR, hdr, resp,
3158c2bfb14SMatt Johnston resp_payload_len);
3168c2bfb14SMatt Johnston }
3178c2bfb14SMatt Johnston
3188c2bfb14SMatt Johnston size_t len = buf->remaining;
3198c2bfb14SMatt Johnston rc = encode_get_firmware_parameters_resp(hdr->instance, &fwp,
3208c2bfb14SMatt Johnston resp, &len);
3218c2bfb14SMatt Johnston if (rc) {
3228c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp,
3238c2bfb14SMatt Johnston resp_payload_len);
3248c2bfb14SMatt Johnston }
3258c2bfb14SMatt Johnston rc = pldm_msgbuf_skip(buf, len);
3268c2bfb14SMatt Johnston if (rc) {
3278c2bfb14SMatt Johnston return rc;
3288c2bfb14SMatt Johnston }
3298c2bfb14SMatt Johnston }
3308c2bfb14SMatt Johnston
3318c2bfb14SMatt Johnston /* Add the component table entries */
3328c2bfb14SMatt Johnston for (uint16_t i = 0; i < entry_count; i++) {
3338c2bfb14SMatt Johnston const struct pldm_firmware_component_standalone *e = entries[i];
3348c2bfb14SMatt Johnston void *out = NULL;
3358c2bfb14SMatt Johnston size_t len;
3368c2bfb14SMatt Johnston
3378c2bfb14SMatt Johnston struct pldm_component_parameter_entry_full comp = {
3388c2bfb14SMatt Johnston .comp_classification = e->comp_classification,
3398c2bfb14SMatt Johnston .comp_identifier = e->comp_identifier,
3408c2bfb14SMatt Johnston .comp_classification_index =
3418c2bfb14SMatt Johnston e->comp_classification_index,
3428c2bfb14SMatt Johnston
3438c2bfb14SMatt Johnston .active_ver = e->active_ver,
3448c2bfb14SMatt Johnston .pending_ver = e->pending_ver,
3458c2bfb14SMatt Johnston
3468c2bfb14SMatt Johnston .comp_activation_methods = e->comp_activation_methods,
3478c2bfb14SMatt Johnston .capabilities_during_update =
3488c2bfb14SMatt Johnston e->capabilities_during_update,
3498c2bfb14SMatt Johnston };
3508c2bfb14SMatt Johnston
3518c2bfb14SMatt Johnston if (pldm_msgbuf_peek_remaining(buf, &out, &len)) {
3528c2bfb14SMatt Johnston return rc;
3538c2bfb14SMatt Johnston }
3548c2bfb14SMatt Johnston rc = encode_get_firmware_parameters_resp_comp_entry(&comp, out,
3558c2bfb14SMatt Johnston &len);
3568c2bfb14SMatt Johnston if (rc) {
3578c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp,
3588c2bfb14SMatt Johnston resp_payload_len);
3598c2bfb14SMatt Johnston }
3608c2bfb14SMatt Johnston rc = pldm_msgbuf_skip(buf, len);
3618c2bfb14SMatt Johnston if (rc) {
3628c2bfb14SMatt Johnston return rc;
3638c2bfb14SMatt Johnston }
3648c2bfb14SMatt Johnston }
3658c2bfb14SMatt Johnston
3668c2bfb14SMatt Johnston return pldm_msgbuf_destroy_used(buf, *resp_payload_len,
3678c2bfb14SMatt Johnston resp_payload_len);
3688c2bfb14SMatt Johnston }
3698c2bfb14SMatt Johnston
3708c2bfb14SMatt Johnston 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)3718c2bfb14SMatt Johnston static int pldm_fd_request_update(struct pldm_fd *fd,
3728c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
3738c2bfb14SMatt Johnston const struct pldm_msg *req,
3748c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
3758c2bfb14SMatt Johnston size_t *resp_payload_len, uint8_t address)
3768c2bfb14SMatt Johnston {
3778c2bfb14SMatt Johnston struct pldm_request_update_req_full upd;
3788c2bfb14SMatt Johnston const struct pldm_request_update_resp resp_data = {
3798c2bfb14SMatt Johnston .fd_meta_data_len = 0,
3808c2bfb14SMatt Johnston .fd_will_send_pkg_data = 0,
3818c2bfb14SMatt Johnston };
3828c2bfb14SMatt Johnston int rc;
3838c2bfb14SMatt Johnston
3848c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_IDLE) {
3858c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_ALREADY_IN_UPDATE_MODE, hdr,
3868c2bfb14SMatt Johnston resp, resp_payload_len);
3878c2bfb14SMatt Johnston }
3888c2bfb14SMatt Johnston
3898c2bfb14SMatt Johnston rc = decode_request_update_req(req, req_payload_len, &upd);
3908c2bfb14SMatt Johnston if (rc) {
3918c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
3928c2bfb14SMatt Johnston }
3938c2bfb14SMatt Johnston
3948c2bfb14SMatt Johnston /* No metadata nor pkg data */
3958c2bfb14SMatt Johnston rc = encode_request_update_resp(hdr->instance, &resp_data, resp,
3968c2bfb14SMatt Johnston resp_payload_len);
3978c2bfb14SMatt Johnston if (rc) {
3988c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
3998c2bfb14SMatt Johnston }
4008c2bfb14SMatt Johnston
4018c2bfb14SMatt Johnston // TODO pass num_of_comp and image_set_ver to application?
4028c2bfb14SMatt Johnston
4038c2bfb14SMatt Johnston fd->max_transfer =
4048c2bfb14SMatt Johnston fd->ops->transfer_size(fd->ops_ctx, upd.max_transfer_size);
4058c2bfb14SMatt Johnston if (fd->max_transfer > upd.max_transfer_size) {
4068c2bfb14SMatt Johnston /* Limit to UA's size */
4078c2bfb14SMatt Johnston fd->max_transfer = upd.max_transfer_size;
4088c2bfb14SMatt Johnston }
4098c2bfb14SMatt Johnston if (fd->max_transfer < PLDM_FWUP_BASELINE_TRANSFER_SIZE) {
4108c2bfb14SMatt Johnston /* Don't let it be zero, that will loop forever */
4118c2bfb14SMatt Johnston fd->max_transfer = PLDM_FWUP_BASELINE_TRANSFER_SIZE;
4128c2bfb14SMatt Johnston }
4138c2bfb14SMatt Johnston fd->ua_address = address;
4148c2bfb14SMatt Johnston fd->ua_address_set = true;
4158c2bfb14SMatt Johnston
4168c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_LEARN_COMPONENTS);
4178c2bfb14SMatt Johnston
4188c2bfb14SMatt Johnston return 0;
4198c2bfb14SMatt Johnston }
4208c2bfb14SMatt Johnston
4218c2bfb14SMatt Johnston /* wrapper around ops->cancel, will only run ops->cancel when a component update is
4228c2bfb14SMatt Johnston * active */
4238c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_maybe_cancel_component(struct pldm_fd * fd)4248c2bfb14SMatt Johnston static void pldm_fd_maybe_cancel_component(struct pldm_fd *fd)
4258c2bfb14SMatt Johnston {
4268c2bfb14SMatt Johnston bool cancel = false;
4278c2bfb14SMatt Johnston
4288c2bfb14SMatt Johnston switch (fd->state) {
4298c2bfb14SMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
4308c2bfb14SMatt Johnston case PLDM_FD_STATE_VERIFY:
4318c2bfb14SMatt Johnston cancel = true;
4328c2bfb14SMatt Johnston break;
4338c2bfb14SMatt Johnston case PLDM_FD_STATE_APPLY:
4348c2bfb14SMatt Johnston /* In apply state, once the application ops->apply() has completed
4358c2bfb14SMatt Johnston * successfully the component is no longer in update state.
4368c2bfb14SMatt Johnston * In that case the cancel should not be forwarded to the application.
4378c2bfb14SMatt Johnston * This can occur if a cancel is received while waiting for the
4388c2bfb14SMatt Johnston * response to a success ApplyComplete. */
4398c2bfb14SMatt Johnston cancel = !(fd->req.complete &&
4408c2bfb14SMatt Johnston fd->req.result == PLDM_FWUP_APPLY_SUCCESS);
4418c2bfb14SMatt Johnston break;
4428c2bfb14SMatt Johnston default:
4438c2bfb14SMatt Johnston break;
4448c2bfb14SMatt Johnston }
4458c2bfb14SMatt Johnston
4468c2bfb14SMatt Johnston if (cancel) {
4478c2bfb14SMatt Johnston /* Call the platform handler for the current component in progress */
4488c2bfb14SMatt Johnston fd->ops->cancel_update_component(fd->ops_ctx, &fd->update_comp);
4498c2bfb14SMatt Johnston }
4508c2bfb14SMatt Johnston }
4518c2bfb14SMatt Johnston
4528c2bfb14SMatt Johnston /* Wrapper around ops->update_component() that first checks that the component
4538c2bfb14SMatt Johnston * is in the list returned from ops->components() */
4548c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_check_update_component(struct pldm_fd * fd,bool update,const struct pldm_firmware_update_component * comp)4558c2bfb14SMatt Johnston static enum pldm_component_response_codes pldm_fd_check_update_component(
4568c2bfb14SMatt Johnston struct pldm_fd *fd, bool update,
4578c2bfb14SMatt Johnston const struct pldm_firmware_update_component *comp)
4588c2bfb14SMatt Johnston {
4598c2bfb14SMatt Johnston bool found;
4608c2bfb14SMatt Johnston uint16_t entry_count;
4618c2bfb14SMatt Johnston int rc;
4628c2bfb14SMatt Johnston
4638c2bfb14SMatt Johnston const struct pldm_firmware_component_standalone **entries;
4648c2bfb14SMatt Johnston rc = fd->ops->components(fd->ops_ctx, &entry_count, &entries);
4658c2bfb14SMatt Johnston if (rc) {
4668c2bfb14SMatt Johnston return PLDM_CRC_COMP_NOT_SUPPORTED;
4678c2bfb14SMatt Johnston }
4688c2bfb14SMatt Johnston
4698c2bfb14SMatt Johnston found = false;
4708c2bfb14SMatt Johnston for (uint16_t i = 0; i < entry_count; i++) {
4718c2bfb14SMatt Johnston if (entries[i]->comp_classification ==
4728c2bfb14SMatt Johnston comp->comp_classification &&
4738c2bfb14SMatt Johnston entries[i]->comp_identifier == comp->comp_identifier &&
4748c2bfb14SMatt Johnston entries[i]->comp_classification_index ==
4758c2bfb14SMatt Johnston comp->comp_classification_index) {
4768c2bfb14SMatt Johnston found = true;
4778c2bfb14SMatt Johnston break;
4788c2bfb14SMatt Johnston }
4798c2bfb14SMatt Johnston }
4808c2bfb14SMatt Johnston if (found) {
4818c2bfb14SMatt Johnston return fd->ops->update_component(fd->ops_ctx, update, comp);
4828c2bfb14SMatt Johnston }
4838c2bfb14SMatt Johnston return PLDM_CRC_COMP_NOT_SUPPORTED;
4848c2bfb14SMatt Johnston }
4858c2bfb14SMatt Johnston
4868c2bfb14SMatt Johnston 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)4878c2bfb14SMatt Johnston static int pldm_fd_pass_comp(struct pldm_fd *fd,
4888c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
4898c2bfb14SMatt Johnston const struct pldm_msg *req, size_t req_payload_len,
4908c2bfb14SMatt Johnston struct pldm_msg *resp, size_t *resp_payload_len)
4918c2bfb14SMatt Johnston {
4928c2bfb14SMatt Johnston struct pldm_pass_component_table_req_full pcomp;
4938c2bfb14SMatt Johnston uint8_t comp_response_code;
4948c2bfb14SMatt Johnston int rc;
4958c2bfb14SMatt Johnston
4968c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_LEARN_COMPONENTS) {
4978c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
4988c2bfb14SMatt Johnston hdr, resp, resp_payload_len);
4998c2bfb14SMatt Johnston }
5008c2bfb14SMatt Johnston
5018c2bfb14SMatt Johnston /* fd->update_comp is used as temporary storage during PassComponent validation */
5028c2bfb14SMatt Johnston /* Some portions are unused for PassComponentTable */
5038c2bfb14SMatt Johnston fd->update_comp.comp_image_size = 0;
5048c2bfb14SMatt Johnston fd->update_comp.update_option_flags.value = 0;
5058c2bfb14SMatt Johnston
5068c2bfb14SMatt Johnston rc = decode_pass_component_table_req(req, req_payload_len, &pcomp);
5078c2bfb14SMatt Johnston if (rc) {
5088c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
5098c2bfb14SMatt Johnston }
5108c2bfb14SMatt Johnston
5118c2bfb14SMatt Johnston fd->update_comp.comp_classification = pcomp.comp_classification;
5128c2bfb14SMatt Johnston fd->update_comp.comp_identifier = pcomp.comp_identifier;
5138c2bfb14SMatt Johnston fd->update_comp.comp_classification_index =
5148c2bfb14SMatt Johnston pcomp.comp_classification_index;
5158c2bfb14SMatt Johnston fd->update_comp.comp_comparison_stamp = pcomp.comp_comparison_stamp;
5168c2bfb14SMatt Johnston memcpy(&fd->update_comp.version, &pcomp.version, sizeof(pcomp.version));
5178c2bfb14SMatt Johnston
5188c2bfb14SMatt Johnston comp_response_code =
5198c2bfb14SMatt Johnston pldm_fd_check_update_component(fd, false, &fd->update_comp);
5208c2bfb14SMatt Johnston
5218c2bfb14SMatt Johnston const struct pldm_pass_component_table_resp resp_data = {
5228c2bfb14SMatt Johnston /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
5238c2bfb14SMatt Johnston .comp_resp = (comp_response_code != 0),
5248c2bfb14SMatt Johnston .comp_resp_code = comp_response_code,
5258c2bfb14SMatt Johnston };
5268c2bfb14SMatt Johnston
5278c2bfb14SMatt Johnston rc = encode_pass_component_table_resp(hdr->instance, &resp_data, resp,
5288c2bfb14SMatt Johnston resp_payload_len);
5298c2bfb14SMatt Johnston if (rc) {
5308c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
5318c2bfb14SMatt Johnston }
5328c2bfb14SMatt Johnston
5338c2bfb14SMatt Johnston if (pcomp.transfer_flag & PLDM_END) {
5348c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
5358c2bfb14SMatt Johnston }
5368c2bfb14SMatt Johnston
5378c2bfb14SMatt Johnston return 0;
5388c2bfb14SMatt Johnston }
5398c2bfb14SMatt Johnston
5408c2bfb14SMatt Johnston 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)5418c2bfb14SMatt Johnston static int pldm_fd_update_comp(struct pldm_fd *fd,
5428c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
5438c2bfb14SMatt Johnston const struct pldm_msg *req,
5448c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
5458c2bfb14SMatt Johnston size_t *resp_payload_len)
5468c2bfb14SMatt Johnston {
5478c2bfb14SMatt Johnston struct pldm_update_component_req_full up;
5488c2bfb14SMatt Johnston uint8_t comp_response_code;
5498c2bfb14SMatt Johnston int rc;
5508c2bfb14SMatt Johnston
5518c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_READY_XFER) {
5528c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
5538c2bfb14SMatt Johnston hdr, resp, resp_payload_len);
5548c2bfb14SMatt Johnston }
5558c2bfb14SMatt Johnston
5568c2bfb14SMatt Johnston rc = decode_update_component_req(req, req_payload_len, &up);
5578c2bfb14SMatt Johnston if (rc) {
5588c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
5598c2bfb14SMatt Johnston }
5608c2bfb14SMatt Johnston
5618c2bfb14SMatt Johnston /* Store update_comp to pass to further callbacks. This persists
5628c2bfb14SMatt Johnston * until the component update completes or is cancelled */
5638c2bfb14SMatt Johnston fd->update_comp.comp_classification = up.comp_classification;
5648c2bfb14SMatt Johnston fd->update_comp.comp_identifier = up.comp_identifier;
5658c2bfb14SMatt Johnston fd->update_comp.comp_classification_index =
5668c2bfb14SMatt Johnston up.comp_classification_index;
5678c2bfb14SMatt Johnston fd->update_comp.comp_comparison_stamp = up.comp_comparison_stamp;
5688c2bfb14SMatt Johnston fd->update_comp.comp_image_size = up.comp_image_size;
5698c2bfb14SMatt Johnston fd->update_comp.update_option_flags = up.update_option_flags;
5708c2bfb14SMatt Johnston memcpy(&fd->update_comp.version, &up.version, sizeof(up.version));
5718c2bfb14SMatt Johnston
5728c2bfb14SMatt Johnston comp_response_code =
5738c2bfb14SMatt Johnston pldm_fd_check_update_component(fd, true, &fd->update_comp);
5748c2bfb14SMatt Johnston
5758c2bfb14SMatt Johnston // Mask to only the "Force Update" flag, others are not handled.
5768c2bfb14SMatt Johnston bitfield32_t update_flags = {
5778c2bfb14SMatt Johnston .bits.bit0 = fd->update_comp.update_option_flags.bits.bit0
5788c2bfb14SMatt Johnston };
5798c2bfb14SMatt Johnston
5808c2bfb14SMatt Johnston const struct pldm_update_component_resp resp_data = {
5818c2bfb14SMatt Johnston /* Component Response Code is 0 for ComponentResponse, 1 otherwise */
5828c2bfb14SMatt Johnston .comp_compatibility_resp = (comp_response_code != 0),
5838c2bfb14SMatt Johnston .comp_compatibility_resp_code = comp_response_code,
5848c2bfb14SMatt Johnston .update_option_flags_enabled = update_flags,
5858c2bfb14SMatt Johnston .time_before_req_fw_data = 0,
5868c2bfb14SMatt Johnston };
5878c2bfb14SMatt Johnston
5888c2bfb14SMatt Johnston rc = encode_update_component_resp(hdr->instance, &resp_data, resp,
5898c2bfb14SMatt Johnston resp_payload_len);
5908c2bfb14SMatt Johnston if (rc) {
5918c2bfb14SMatt Johnston /* Encoding response failed */
5928c2bfb14SMatt Johnston if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
5938c2bfb14SMatt Johnston /* Inform the application of cancellation. Call it directly
5948c2bfb14SMatt Johnston * rather than going through pldm_fd_maybe_cancel_component() */
5958c2bfb14SMatt Johnston fd->ops->cancel_update_component(fd->ops_ctx,
5968c2bfb14SMatt Johnston &fd->update_comp);
5978c2bfb14SMatt Johnston }
5988c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
5998c2bfb14SMatt Johnston }
6008c2bfb14SMatt Johnston
6018c2bfb14SMatt Johnston /* Set up download state */
6028c2bfb14SMatt Johnston if (comp_response_code == PLDM_CRC_COMP_CAN_BE_UPDATED) {
6038c2bfb14SMatt Johnston memset(&fd->specific, 0x0, sizeof(fd->specific));
6048c2bfb14SMatt Johnston fd->update_flags = update_flags;
6058c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_READY;
6068c2bfb14SMatt Johnston fd->req.complete = false;
6078c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_DOWNLOAD);
6088c2bfb14SMatt Johnston }
6098c2bfb14SMatt Johnston
6108c2bfb14SMatt Johnston return 0;
6118c2bfb14SMatt Johnston }
6128c2bfb14SMatt Johnston
6138c2bfb14SMatt Johnston 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)6148c2bfb14SMatt Johnston static int pldm_fd_get_status(struct pldm_fd *fd,
6158c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
6168c2bfb14SMatt Johnston const struct pldm_msg *req LIBPLDM_CC_UNUSED,
6178c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
6188c2bfb14SMatt Johnston size_t *resp_payload_len)
6198c2bfb14SMatt Johnston {
6208c2bfb14SMatt Johnston int rc;
6218c2bfb14SMatt Johnston
6228c2bfb14SMatt Johnston /* No request data */
6238c2bfb14SMatt Johnston if (req_payload_len != PLDM_GET_STATUS_REQ_BYTES) {
6248c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
6258c2bfb14SMatt Johnston resp_payload_len);
6268c2bfb14SMatt Johnston }
6278c2bfb14SMatt Johnston
6288c2bfb14SMatt Johnston /* Defaults */
6298c2bfb14SMatt Johnston struct pldm_get_status_resp st = {
6308c2bfb14SMatt Johnston .current_state = fd->state,
6318c2bfb14SMatt Johnston .previous_state = fd->prev_state,
6328c2bfb14SMatt Johnston .progress_percent = PROGRESS_PERCENT_NOT_SUPPORTED,
6338c2bfb14SMatt Johnston };
6348c2bfb14SMatt Johnston
6358c2bfb14SMatt Johnston pldm_fd_get_aux_state(fd, &st.aux_state, &st.aux_state_status);
6368c2bfb14SMatt Johnston
6378c2bfb14SMatt Johnston switch (fd->state) {
6388c2bfb14SMatt Johnston case PLDM_FD_STATE_IDLE:
6398c2bfb14SMatt Johnston st.reason_code = fd->reason;
6408c2bfb14SMatt Johnston break;
6418c2bfb14SMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
6428c2bfb14SMatt Johnston if (fd->update_comp.comp_image_size > 0) {
6438c2bfb14SMatt Johnston uint32_t one_percent =
6448c2bfb14SMatt Johnston fd->update_comp.comp_image_size / 100;
6458c2bfb14SMatt Johnston if (fd->update_comp.comp_image_size % 100 != 0) {
6468c2bfb14SMatt Johnston one_percent += 1;
6478c2bfb14SMatt Johnston }
6488c2bfb14SMatt Johnston st.progress_percent =
6498c2bfb14SMatt Johnston (fd->specific.download.offset / one_percent);
6508c2bfb14SMatt Johnston }
6518c2bfb14SMatt Johnston st.update_option_flags_enabled = fd->update_flags;
6528c2bfb14SMatt Johnston break;
6538c2bfb14SMatt Johnston case PLDM_FD_STATE_VERIFY:
6548c2bfb14SMatt Johnston st.update_option_flags_enabled = fd->update_flags;
6558c2bfb14SMatt Johnston st.progress_percent = fd->specific.verify.progress_percent;
6568c2bfb14SMatt Johnston break;
6578c2bfb14SMatt Johnston case PLDM_FD_STATE_APPLY:
6588c2bfb14SMatt Johnston st.update_option_flags_enabled = fd->update_flags;
6598c2bfb14SMatt Johnston st.progress_percent = fd->specific.apply.progress_percent;
6608c2bfb14SMatt Johnston break;
6618c2bfb14SMatt Johnston default:
6628c2bfb14SMatt Johnston break;
6638c2bfb14SMatt Johnston }
6648c2bfb14SMatt Johnston
6658c2bfb14SMatt Johnston rc = encode_get_status_resp(hdr->instance, &st, resp, resp_payload_len);
6668c2bfb14SMatt Johnston if (rc) {
6678c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
6688c2bfb14SMatt Johnston }
6698c2bfb14SMatt Johnston
6708c2bfb14SMatt Johnston return 0;
6718c2bfb14SMatt Johnston }
6728c2bfb14SMatt Johnston
6738c2bfb14SMatt Johnston 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)6748c2bfb14SMatt Johnston static int pldm_fd_cancel_update_comp(
6758c2bfb14SMatt Johnston struct pldm_fd *fd, const struct pldm_header_info *hdr,
6768c2bfb14SMatt Johnston const struct pldm_msg *req LIBPLDM_CC_UNUSED, size_t req_payload_len,
6778c2bfb14SMatt Johnston struct pldm_msg *resp, size_t *resp_payload_len)
6788c2bfb14SMatt Johnston {
6798c2bfb14SMatt Johnston int rc;
6808c2bfb14SMatt Johnston
6818c2bfb14SMatt Johnston /* No request data */
6828c2bfb14SMatt Johnston if (req_payload_len != PLDM_CANCEL_UPDATE_COMPONENT_REQ_BYTES) {
6838c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
6848c2bfb14SMatt Johnston resp_payload_len);
6858c2bfb14SMatt Johnston }
6868c2bfb14SMatt Johnston
6878c2bfb14SMatt Johnston switch (fd->state) {
6888c2bfb14SMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
6898c2bfb14SMatt Johnston case PLDM_FD_STATE_VERIFY:
6908c2bfb14SMatt Johnston case PLDM_FD_STATE_APPLY:
6918c2bfb14SMatt Johnston break;
6928c2bfb14SMatt Johnston default:
6938c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
6948c2bfb14SMatt Johnston resp_payload_len);
6958c2bfb14SMatt Johnston }
6968c2bfb14SMatt Johnston
6978c2bfb14SMatt Johnston /* No response payload */
6988c2bfb14SMatt Johnston rc = pldm_fd_reply_cc(PLDM_SUCCESS, hdr, resp, resp_payload_len);
6998c2bfb14SMatt Johnston if (rc) {
7008c2bfb14SMatt Johnston return rc;
7018c2bfb14SMatt Johnston }
7028c2bfb14SMatt Johnston
7038c2bfb14SMatt Johnston pldm_fd_maybe_cancel_component(fd);
7048c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
7058c2bfb14SMatt Johnston
7068c2bfb14SMatt Johnston return 0;
7078c2bfb14SMatt Johnston }
7088c2bfb14SMatt Johnston
7098c2bfb14SMatt Johnston 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)7108c2bfb14SMatt Johnston static int pldm_fd_cancel_update(struct pldm_fd *fd,
7118c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
7128c2bfb14SMatt Johnston const struct pldm_msg *req LIBPLDM_CC_UNUSED,
7138c2bfb14SMatt Johnston size_t req_payload_len, struct pldm_msg *resp,
7148c2bfb14SMatt Johnston size_t *resp_payload_len)
7158c2bfb14SMatt Johnston {
7168c2bfb14SMatt Johnston int rc;
7178c2bfb14SMatt Johnston
7188c2bfb14SMatt Johnston /* No request data */
7198c2bfb14SMatt Johnston if (req_payload_len != PLDM_CANCEL_UPDATE_REQ_BYTES) {
7208c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_INVALID_LENGTH, hdr, resp,
7218c2bfb14SMatt Johnston resp_payload_len);
7228c2bfb14SMatt Johnston }
7238c2bfb14SMatt Johnston
7248c2bfb14SMatt Johnston if (fd->state == PLDM_FD_STATE_IDLE) {
7258c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_NOT_IN_UPDATE_MODE, hdr, resp,
7268c2bfb14SMatt Johnston resp_payload_len);
7278c2bfb14SMatt Johnston }
7288c2bfb14SMatt Johnston
7298c2bfb14SMatt Johnston /* Assume non_functioning_component_indication = False, in future
7308c2bfb14SMatt Johnston * could add a platform callback */
7318c2bfb14SMatt Johnston const struct pldm_cancel_update_resp resp_data = {
7328c2bfb14SMatt Johnston .non_functioning_component_indication = 0,
7338c2bfb14SMatt Johnston .non_functioning_component_bitmap = 0,
7348c2bfb14SMatt Johnston };
7358c2bfb14SMatt Johnston rc = encode_cancel_update_resp(hdr->instance, &resp_data, resp,
7368c2bfb14SMatt Johnston resp_payload_len);
7378c2bfb14SMatt Johnston if (rc) {
7388c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
7398c2bfb14SMatt Johnston }
7408c2bfb14SMatt Johnston
7418c2bfb14SMatt Johnston pldm_fd_maybe_cancel_component(fd);
7428c2bfb14SMatt Johnston pldm_fd_set_idle(fd, PLDM_FD_CANCEL_UPDATE);
7438c2bfb14SMatt Johnston
7448c2bfb14SMatt Johnston return 0;
7458c2bfb14SMatt Johnston }
7468c2bfb14SMatt Johnston
7478c2bfb14SMatt Johnston 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)7488c2bfb14SMatt Johnston static int pldm_fd_activate_firmware(struct pldm_fd *fd,
7498c2bfb14SMatt Johnston const struct pldm_header_info *hdr,
7508c2bfb14SMatt Johnston const struct pldm_msg *req,
7518c2bfb14SMatt Johnston size_t req_payload_len,
7528c2bfb14SMatt Johnston struct pldm_msg *resp,
7538c2bfb14SMatt Johnston size_t *resp_payload_len)
7548c2bfb14SMatt Johnston {
7558c2bfb14SMatt Johnston uint16_t estimated_time;
7568c2bfb14SMatt Johnston uint8_t ccode;
7578c2bfb14SMatt Johnston int rc;
7588c2bfb14SMatt Johnston bool self_contained;
7598c2bfb14SMatt Johnston
7608c2bfb14SMatt Johnston rc = decode_activate_firmware_req(req, req_payload_len,
7618c2bfb14SMatt Johnston &self_contained);
7628c2bfb14SMatt Johnston if (rc) {
7638c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp, resp_payload_len);
7648c2bfb14SMatt Johnston }
7658c2bfb14SMatt Johnston
7668c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_READY_XFER) {
7678c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_FWUP_INVALID_STATE_FOR_COMMAND,
7688c2bfb14SMatt Johnston hdr, resp, resp_payload_len);
7698c2bfb14SMatt Johnston }
7708c2bfb14SMatt Johnston
7718c2bfb14SMatt Johnston estimated_time = 0;
7728c2bfb14SMatt Johnston ccode = fd->ops->activate(fd->ops_ctx, self_contained, &estimated_time);
7738c2bfb14SMatt Johnston
7748c2bfb14SMatt Johnston if (ccode == PLDM_SUCCESS ||
7758c2bfb14SMatt Johnston ccode == PLDM_FWUP_ACTIVATION_NOT_REQUIRED) {
7768c2bfb14SMatt Johnston /* Transition through states so that the prev_state is correct */
7778c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_ACTIVATE);
7788c2bfb14SMatt Johnston pldm_fd_set_idle(fd, PLDM_FD_ACTIVATE_FW);
7798c2bfb14SMatt Johnston }
7808c2bfb14SMatt Johnston
7818c2bfb14SMatt Johnston if (ccode == PLDM_SUCCESS) {
7828c2bfb14SMatt Johnston const struct pldm_activate_firmware_resp resp_data = {
7838c2bfb14SMatt Johnston .estimated_time_activation = estimated_time,
7848c2bfb14SMatt Johnston };
7858c2bfb14SMatt Johnston rc = encode_activate_firmware_resp(hdr->instance, &resp_data,
7868c2bfb14SMatt Johnston resp, resp_payload_len);
7878c2bfb14SMatt Johnston if (rc) {
7888c2bfb14SMatt Johnston return pldm_fd_reply_errno(rc, hdr, resp,
7898c2bfb14SMatt Johnston resp_payload_len);
7908c2bfb14SMatt Johnston }
7918c2bfb14SMatt Johnston } else {
7928c2bfb14SMatt Johnston return pldm_fd_reply_cc(ccode, hdr, resp, resp_payload_len);
7938c2bfb14SMatt Johnston }
7948c2bfb14SMatt Johnston
7958c2bfb14SMatt Johnston return 0;
7968c2bfb14SMatt Johnston }
7978c2bfb14SMatt Johnston
7988c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_fwdata_size(struct pldm_fd * fd)7998c2bfb14SMatt Johnston static uint32_t pldm_fd_fwdata_size(struct pldm_fd *fd)
8008c2bfb14SMatt Johnston {
8018c2bfb14SMatt Johnston uint32_t size;
8028c2bfb14SMatt Johnston
8038c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
8048c2bfb14SMatt Johnston assert(false);
8058c2bfb14SMatt Johnston return 0;
8068c2bfb14SMatt Johnston }
8078c2bfb14SMatt Johnston
8088c2bfb14SMatt Johnston if (fd->specific.download.offset > fd->update_comp.comp_image_size) {
8098c2bfb14SMatt Johnston assert(false);
8108c2bfb14SMatt Johnston return 0;
8118c2bfb14SMatt Johnston }
8128c2bfb14SMatt Johnston size = fd->update_comp.comp_image_size - fd->specific.download.offset;
8138c2bfb14SMatt Johnston
8148c2bfb14SMatt Johnston if (size > fd->max_transfer) {
8158c2bfb14SMatt Johnston size = fd->max_transfer;
8168c2bfb14SMatt Johnston }
8178c2bfb14SMatt Johnston return size;
8188c2bfb14SMatt Johnston }
8198c2bfb14SMatt Johnston
8208c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_handle_fwdata_resp(struct pldm_fd * fd,const struct pldm_msg * resp,size_t resp_payload_len)8218c2bfb14SMatt Johnston static int pldm_fd_handle_fwdata_resp(struct pldm_fd *fd,
8228c2bfb14SMatt Johnston const struct pldm_msg *resp,
8238c2bfb14SMatt Johnston size_t resp_payload_len)
8248c2bfb14SMatt Johnston {
8258c2bfb14SMatt Johnston struct pldm_fd_download *dl;
8268c2bfb14SMatt Johnston uint32_t fwdata_size;
8278c2bfb14SMatt Johnston uint8_t res;
8288c2bfb14SMatt Johnston
8298c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
8308c2bfb14SMatt Johnston return -EPROTO;
8318c2bfb14SMatt Johnston }
8328c2bfb14SMatt Johnston
8338c2bfb14SMatt Johnston if (fd->req.state != PLDM_FD_REQ_SENT) {
8348c2bfb14SMatt Johnston /* Not waiting for a response, ignore it */
8358c2bfb14SMatt Johnston return -EPROTO;
8368c2bfb14SMatt Johnston }
8378c2bfb14SMatt Johnston
8388c2bfb14SMatt Johnston dl = &fd->specific.download;
8398c2bfb14SMatt Johnston if (fd->req.complete) {
8408c2bfb14SMatt Johnston /* Received data after completion */
8418c2bfb14SMatt Johnston return -EPROTO;
8428c2bfb14SMatt Johnston }
8438c2bfb14SMatt Johnston
8448c2bfb14SMatt Johnston switch (resp->payload[0]) {
8458c2bfb14SMatt Johnston case PLDM_SUCCESS:
8468c2bfb14SMatt Johnston break;
8478c2bfb14SMatt Johnston case PLDM_FWUP_RETRY_REQUEST_FW_DATA:
8488c2bfb14SMatt Johnston /* Just return, let the retry timer send another request later */
8498c2bfb14SMatt Johnston return 0;
8508c2bfb14SMatt Johnston default:
8518c2bfb14SMatt Johnston /* Send a TransferComplete failure */
8528c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_READY;
8538c2bfb14SMatt Johnston fd->req.complete = true;
8548c2bfb14SMatt Johnston fd->req.result = PLDM_FWUP_FD_ABORTED_TRANSFER;
8558c2bfb14SMatt Johnston return 0;
8568c2bfb14SMatt Johnston }
8578c2bfb14SMatt Johnston
8588c2bfb14SMatt Johnston /* Handle the received data */
8598c2bfb14SMatt Johnston
8608c2bfb14SMatt Johnston fwdata_size = pldm_fd_fwdata_size(fd);
8618c2bfb14SMatt Johnston if (resp_payload_len != fwdata_size + 1) {
8628c2bfb14SMatt Johnston /* Data is incorrect size. Could indicate MCTP corruption, drop it
8638c2bfb14SMatt Johnston * and let retry timer handle it */
8648c2bfb14SMatt Johnston return -EOVERFLOW;
8658c2bfb14SMatt Johnston }
8668c2bfb14SMatt Johnston
8678c2bfb14SMatt Johnston /* Check pldm_fd_fwdata_size calculation, should not fail */
8688c2bfb14SMatt Johnston if (dl->offset + fwdata_size < dl->offset ||
8698c2bfb14SMatt Johnston dl->offset + fwdata_size > fd->update_comp.comp_image_size) {
8708c2bfb14SMatt Johnston assert(false);
8718c2bfb14SMatt Johnston return -EINVAL;
8728c2bfb14SMatt Johnston }
8738c2bfb14SMatt Johnston
8748c2bfb14SMatt Johnston /* Provide the data chunk to the device */
8758c2bfb14SMatt Johnston res = fd->ops->firmware_data(fd->ops_ctx, dl->offset, &resp->payload[1],
8768c2bfb14SMatt Johnston fwdata_size, &fd->update_comp);
8778c2bfb14SMatt Johnston
8788c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_READY;
8798c2bfb14SMatt Johnston if (res == PLDM_FWUP_TRANSFER_SUCCESS) {
8808c2bfb14SMatt Johnston /* Move to next offset */
8818c2bfb14SMatt Johnston dl->offset += fwdata_size;
8828c2bfb14SMatt Johnston if (dl->offset == fd->update_comp.comp_image_size) {
8838c2bfb14SMatt Johnston /* Mark as complete, next progress() call will send the TransferComplete request */
8848c2bfb14SMatt Johnston fd->req.complete = true;
8858c2bfb14SMatt Johnston fd->req.result = PLDM_FWUP_TRANSFER_SUCCESS;
8868c2bfb14SMatt Johnston }
8878c2bfb14SMatt Johnston } else {
8888c2bfb14SMatt Johnston /* Pass the callback error as the TransferResult */
8898c2bfb14SMatt Johnston fd->req.complete = true;
8908c2bfb14SMatt Johnston fd->req.result = res;
8918c2bfb14SMatt Johnston }
8928c2bfb14SMatt Johnston
8938c2bfb14SMatt Johnston return 0;
8948c2bfb14SMatt Johnston }
8958c2bfb14SMatt Johnston
8968c2bfb14SMatt Johnston 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)8978c2bfb14SMatt Johnston static int pldm_fd_handle_transfer_complete_resp(
8988c2bfb14SMatt Johnston struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
8998c2bfb14SMatt Johnston size_t resp_payload_len LIBPLDM_CC_UNUSED)
9008c2bfb14SMatt Johnston {
9018c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_DOWNLOAD) {
9028c2bfb14SMatt Johnston return -EPROTO;
9038c2bfb14SMatt Johnston }
9048c2bfb14SMatt Johnston
9058c2bfb14SMatt Johnston if (fd->req.state != PLDM_FD_REQ_SENT) {
9068c2bfb14SMatt Johnston /* Not waiting for a response, ignore it */
9078c2bfb14SMatt Johnston return -EPROTO;
9088c2bfb14SMatt Johnston }
9098c2bfb14SMatt Johnston
9108c2bfb14SMatt Johnston if (!fd->req.complete) {
9118c2bfb14SMatt Johnston /* Were waiting for RequestFirmwareData instead, ignore it */
9128c2bfb14SMatt Johnston return -EPROTO;
9138c2bfb14SMatt Johnston }
9148c2bfb14SMatt Johnston
9158c2bfb14SMatt Johnston /* Disregard the response completion code */
9168c2bfb14SMatt Johnston
9178c2bfb14SMatt Johnston /* Next state depends whether the transfer succeeded */
9188c2bfb14SMatt Johnston if (fd->req.result == PLDM_FWUP_TRANSFER_SUCCESS) {
9198c2bfb14SMatt Johnston /* Switch to Verify */
9208c2bfb14SMatt Johnston memset(&fd->specific, 0x0, sizeof(fd->specific));
9218c2bfb14SMatt Johnston fd->specific.verify.progress_percent =
9228c2bfb14SMatt Johnston PROGRESS_PERCENT_NOT_SUPPORTED;
9238c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_READY;
9248c2bfb14SMatt Johnston fd->req.complete = false;
9258c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_VERIFY);
9268c2bfb14SMatt Johnston } else {
9278c2bfb14SMatt Johnston /* Wait for UA to cancel */
9288c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_FAILED;
9298c2bfb14SMatt Johnston }
9308c2bfb14SMatt Johnston return 0;
9318c2bfb14SMatt Johnston }
9328c2bfb14SMatt Johnston
9338c2bfb14SMatt Johnston 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)9348c2bfb14SMatt Johnston static int pldm_fd_handle_verify_complete_resp(
9358c2bfb14SMatt Johnston struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
9368c2bfb14SMatt Johnston size_t resp_payload_len LIBPLDM_CC_UNUSED)
9378c2bfb14SMatt Johnston {
9388c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_VERIFY) {
9398c2bfb14SMatt Johnston return -EPROTO;
9408c2bfb14SMatt Johnston }
9418c2bfb14SMatt Johnston
9428c2bfb14SMatt Johnston if (fd->req.state != PLDM_FD_REQ_SENT) {
9438c2bfb14SMatt Johnston /* Not waiting for a response, ignore it */
9448c2bfb14SMatt Johnston return -EPROTO;
9458c2bfb14SMatt Johnston }
9468c2bfb14SMatt Johnston
9478c2bfb14SMatt Johnston assert(fd->req.complete);
9488c2bfb14SMatt Johnston
9498c2bfb14SMatt Johnston /* Disregard the response completion code */
9508c2bfb14SMatt Johnston
9518c2bfb14SMatt Johnston /* Next state depends whether the verify succeeded */
9528c2bfb14SMatt Johnston if (fd->req.result == PLDM_FWUP_VERIFY_SUCCESS) {
9538c2bfb14SMatt Johnston /* Switch to Apply */
9548c2bfb14SMatt Johnston memset(&fd->specific, 0x0, sizeof(fd->specific));
9558c2bfb14SMatt Johnston fd->specific.apply.progress_percent =
9568c2bfb14SMatt Johnston PROGRESS_PERCENT_NOT_SUPPORTED;
9578c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_READY;
9588c2bfb14SMatt Johnston fd->req.complete = false;
9598c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_APPLY);
9608c2bfb14SMatt Johnston } else {
9618c2bfb14SMatt Johnston /* Wait for UA to cancel */
9628c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_FAILED;
9638c2bfb14SMatt Johnston }
9648c2bfb14SMatt Johnston return 0;
9658c2bfb14SMatt Johnston }
9668c2bfb14SMatt Johnston
9678c2bfb14SMatt Johnston 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)9688c2bfb14SMatt Johnston static int pldm_fd_handle_apply_complete_resp(
9698c2bfb14SMatt Johnston struct pldm_fd *fd, const struct pldm_msg *resp LIBPLDM_CC_UNUSED,
9708c2bfb14SMatt Johnston size_t resp_payload_len LIBPLDM_CC_UNUSED)
9718c2bfb14SMatt Johnston {
9728c2bfb14SMatt Johnston if (fd->state != PLDM_FD_STATE_APPLY) {
9738c2bfb14SMatt Johnston return -EPROTO;
9748c2bfb14SMatt Johnston }
9758c2bfb14SMatt Johnston
9768c2bfb14SMatt Johnston if (fd->req.state != PLDM_FD_REQ_SENT) {
9778c2bfb14SMatt Johnston /* Not waiting for a response, ignore it */
9788c2bfb14SMatt Johnston return -EPROTO;
9798c2bfb14SMatt Johnston }
9808c2bfb14SMatt Johnston
9818c2bfb14SMatt Johnston assert(fd->req.complete);
9828c2bfb14SMatt Johnston
9838c2bfb14SMatt Johnston /* Disregard the response completion code */
9848c2bfb14SMatt Johnston
9858c2bfb14SMatt Johnston /* Next state depends whether the apply succeeded */
9868c2bfb14SMatt Johnston if (fd->req.result == PLDM_FWUP_APPLY_SUCCESS) {
9878c2bfb14SMatt Johnston /* Switch to ReadyXfer */
9888c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_UNUSED;
9898c2bfb14SMatt Johnston pldm_fd_set_state(fd, PLDM_FD_STATE_READY_XFER);
9908c2bfb14SMatt Johnston } else {
9918c2bfb14SMatt Johnston /* Wait for UA to cancel */
9928c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_FAILED;
9938c2bfb14SMatt Johnston }
9948c2bfb14SMatt Johnston return 0;
9958c2bfb14SMatt Johnston }
9968c2bfb14SMatt Johnston
9978c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_handle_resp(struct pldm_fd * fd,pldm_tid_t address,const void * resp_msg,size_t resp_len)9988c2bfb14SMatt Johnston static int pldm_fd_handle_resp(struct pldm_fd *fd, pldm_tid_t address,
9998c2bfb14SMatt Johnston const void *resp_msg, size_t resp_len)
10008c2bfb14SMatt Johnston {
10018c2bfb14SMatt Johnston size_t resp_payload_len;
10028c2bfb14SMatt Johnston const struct pldm_msg *resp = resp_msg;
10038c2bfb14SMatt Johnston
10048c2bfb14SMatt Johnston if (!(fd->ua_address_set && fd->ua_address == address)) {
10058c2bfb14SMatt Johnston // Either an early response, or a response from a wrong TID */
10068c2bfb14SMatt Johnston return -EBUSY;
10078c2bfb14SMatt Johnston }
10088c2bfb14SMatt Johnston
10098c2bfb14SMatt Johnston /* Must have a ccode */
10108c2bfb14SMatt Johnston if (resp_len < sizeof(struct pldm_msg_hdr) + 1) {
10118c2bfb14SMatt Johnston return -EINVAL;
10128c2bfb14SMatt Johnston }
10138c2bfb14SMatt Johnston resp_payload_len = resp_len - sizeof(struct pldm_msg_hdr);
10148c2bfb14SMatt Johnston
10158c2bfb14SMatt Johnston if (fd->req.state != PLDM_FD_REQ_SENT) {
10168c2bfb14SMatt Johnston // No response was expected
10178c2bfb14SMatt Johnston return -EPROTO;
10188c2bfb14SMatt Johnston }
10198c2bfb14SMatt Johnston
10208c2bfb14SMatt Johnston if (fd->req.instance_id != resp->hdr.instance_id) {
10218c2bfb14SMatt Johnston // Response wasn't for the expected request
10228c2bfb14SMatt Johnston return -EPROTO;
10238c2bfb14SMatt Johnston }
10248c2bfb14SMatt Johnston if (fd->req.command != resp->hdr.command) {
10258c2bfb14SMatt Johnston // Response wasn't for the expected request
10268c2bfb14SMatt Johnston return -EPROTO;
10278c2bfb14SMatt Johnston }
10288c2bfb14SMatt Johnston
10298c2bfb14SMatt Johnston fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
10308c2bfb14SMatt Johnston
10318c2bfb14SMatt Johnston switch (resp->hdr.command) {
10328c2bfb14SMatt Johnston case PLDM_REQUEST_FIRMWARE_DATA:
10338c2bfb14SMatt Johnston return pldm_fd_handle_fwdata_resp(fd, resp, resp_payload_len);
10348c2bfb14SMatt Johnston break;
10358c2bfb14SMatt Johnston case PLDM_TRANSFER_COMPLETE:
10368c2bfb14SMatt Johnston return pldm_fd_handle_transfer_complete_resp(fd, resp,
10378c2bfb14SMatt Johnston resp_payload_len);
10388c2bfb14SMatt Johnston break;
10398c2bfb14SMatt Johnston case PLDM_VERIFY_COMPLETE:
10408c2bfb14SMatt Johnston return pldm_fd_handle_verify_complete_resp(fd, resp,
10418c2bfb14SMatt Johnston resp_payload_len);
10428c2bfb14SMatt Johnston break;
10438c2bfb14SMatt Johnston case PLDM_APPLY_COMPLETE:
10448c2bfb14SMatt Johnston return pldm_fd_handle_apply_complete_resp(fd, resp,
10458c2bfb14SMatt Johnston resp_payload_len);
10468c2bfb14SMatt Johnston break;
10478c2bfb14SMatt Johnston default:
10488c2bfb14SMatt Johnston /* Unsolicited response. Already compared to command above */
10498c2bfb14SMatt Johnston assert(false);
10508c2bfb14SMatt Johnston return -EINVAL;
10518c2bfb14SMatt Johnston }
10528c2bfb14SMatt Johnston }
10538c2bfb14SMatt Johnston
10548c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_progress_download(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)10558c2bfb14SMatt Johnston static int pldm_fd_progress_download(struct pldm_fd *fd, struct pldm_msg *req,
10568c2bfb14SMatt Johnston size_t *req_payload_len)
10578c2bfb14SMatt Johnston {
10588c2bfb14SMatt Johnston uint8_t instance_id;
10598c2bfb14SMatt Johnston struct pldm_fd_download *dl;
10608c2bfb14SMatt Johnston int rc;
10618c2bfb14SMatt Johnston
10628c2bfb14SMatt Johnston if (!pldm_fd_req_should_send(fd)) {
10638c2bfb14SMatt Johnston /* Nothing to do */
10648c2bfb14SMatt Johnston *req_payload_len = 0;
10658c2bfb14SMatt Johnston return 0;
10668c2bfb14SMatt Johnston }
10678c2bfb14SMatt Johnston
10688c2bfb14SMatt Johnston instance_id = pldm_fd_req_next_instance(&fd->req);
10698c2bfb14SMatt Johnston dl = &fd->specific.download;
10708c2bfb14SMatt Johnston if (fd->req.complete) {
10718c2bfb14SMatt Johnston /* Send TransferComplete */
10728c2bfb14SMatt Johnston rc = encode_transfer_complete_req(instance_id, fd->req.result,
10738c2bfb14SMatt Johnston req, req_payload_len);
10748c2bfb14SMatt Johnston } else {
10758c2bfb14SMatt Johnston /* Send a new RequestFirmwareData */
10768c2bfb14SMatt Johnston const struct pldm_request_firmware_data_req req_params = {
10778c2bfb14SMatt Johnston .offset = dl->offset,
10788c2bfb14SMatt Johnston .length = pldm_fd_fwdata_size(fd),
10798c2bfb14SMatt Johnston };
10808c2bfb14SMatt Johnston
10818c2bfb14SMatt Johnston rc = encode_request_firmware_data_req(instance_id, &req_params,
10828c2bfb14SMatt Johnston req, req_payload_len);
10838c2bfb14SMatt Johnston }
10848c2bfb14SMatt Johnston
10858c2bfb14SMatt Johnston if (rc) {
10868c2bfb14SMatt Johnston return rc;
10878c2bfb14SMatt Johnston }
10888c2bfb14SMatt Johnston
10898c2bfb14SMatt Johnston /* Wait for response */
10908c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_SENT;
10918c2bfb14SMatt Johnston fd->req.instance_id = req->hdr.instance_id;
10928c2bfb14SMatt Johnston fd->req.command = req->hdr.command;
10938c2bfb14SMatt Johnston fd->req.sent_time = pldm_fd_now(fd);
10948c2bfb14SMatt Johnston
10958c2bfb14SMatt Johnston return 0;
10968c2bfb14SMatt Johnston }
10978c2bfb14SMatt Johnston
10988c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_progress_verify(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)10998c2bfb14SMatt Johnston static int pldm_fd_progress_verify(struct pldm_fd *fd, struct pldm_msg *req,
11008c2bfb14SMatt Johnston size_t *req_payload_len)
11018c2bfb14SMatt Johnston {
11028c2bfb14SMatt Johnston uint8_t instance_id;
11038c2bfb14SMatt Johnston int rc;
11048c2bfb14SMatt Johnston
11058c2bfb14SMatt Johnston if (!pldm_fd_req_should_send(fd)) {
11068c2bfb14SMatt Johnston /* Nothing to do */
11078c2bfb14SMatt Johnston *req_payload_len = 0;
11088c2bfb14SMatt Johnston return 0;
11098c2bfb14SMatt Johnston }
11108c2bfb14SMatt Johnston
11118c2bfb14SMatt Johnston if (!fd->req.complete) {
11128c2bfb14SMatt Johnston bool pending = false;
11138c2bfb14SMatt Johnston uint8_t res;
11148c2bfb14SMatt Johnston res = fd->ops->verify(fd->ops_ctx, &fd->update_comp, &pending,
11158c2bfb14SMatt Johnston &fd->specific.verify.progress_percent);
11168c2bfb14SMatt Johnston if (pending) {
11178c2bfb14SMatt Johnston if (res == PLDM_FWUP_VERIFY_SUCCESS) {
11188c2bfb14SMatt Johnston /* Return without a VerifyComplete request.
11198c2bfb14SMatt Johnston * Will call verify() again on next call */
11208c2bfb14SMatt Johnston *req_payload_len = 0;
11218c2bfb14SMatt Johnston return 0;
11228c2bfb14SMatt Johnston }
11238c2bfb14SMatt Johnston /* This is an API infraction by the implementer, return a distinctive failure */
11248c2bfb14SMatt Johnston res = PLDM_FWUP_VENDOR_VERIFY_RESULT_RANGE_MAX;
11258c2bfb14SMatt Johnston }
11268c2bfb14SMatt Johnston fd->req.result = res;
11278c2bfb14SMatt Johnston fd->req.complete = true;
11288c2bfb14SMatt Johnston }
11298c2bfb14SMatt Johnston
11308c2bfb14SMatt Johnston instance_id = pldm_fd_req_next_instance(&fd->req);
11318c2bfb14SMatt Johnston rc = encode_verify_complete_req(instance_id, fd->req.result, req,
11328c2bfb14SMatt Johnston req_payload_len);
11338c2bfb14SMatt Johnston if (rc) {
11348c2bfb14SMatt Johnston return rc;
11358c2bfb14SMatt Johnston }
11368c2bfb14SMatt Johnston
11378c2bfb14SMatt Johnston /* Wait for response */
11388c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_SENT;
11398c2bfb14SMatt Johnston fd->req.instance_id = req->hdr.instance_id;
11408c2bfb14SMatt Johnston fd->req.command = req->hdr.command;
11418c2bfb14SMatt Johnston fd->req.sent_time = pldm_fd_now(fd);
11428c2bfb14SMatt Johnston
11438c2bfb14SMatt Johnston return 0;
11448c2bfb14SMatt Johnston }
11458c2bfb14SMatt Johnston
11468c2bfb14SMatt Johnston LIBPLDM_CC_NONNULL
pldm_fd_progress_apply(struct pldm_fd * fd,struct pldm_msg * req,size_t * req_payload_len)11478c2bfb14SMatt Johnston static int pldm_fd_progress_apply(struct pldm_fd *fd, struct pldm_msg *req,
11488c2bfb14SMatt Johnston size_t *req_payload_len)
11498c2bfb14SMatt Johnston {
11508c2bfb14SMatt Johnston uint8_t instance_id;
11518c2bfb14SMatt Johnston int rc;
11528c2bfb14SMatt Johnston
11538c2bfb14SMatt Johnston if (!pldm_fd_req_should_send(fd)) {
11548c2bfb14SMatt Johnston /* Nothing to do */
11558c2bfb14SMatt Johnston *req_payload_len = 0;
11568c2bfb14SMatt Johnston return 0;
11578c2bfb14SMatt Johnston }
11588c2bfb14SMatt Johnston
11598c2bfb14SMatt Johnston if (!fd->req.complete) {
11608c2bfb14SMatt Johnston bool pending = false;
11618c2bfb14SMatt Johnston uint8_t res;
11628c2bfb14SMatt Johnston res = fd->ops->apply(fd->ops_ctx, &fd->update_comp, &pending,
11638c2bfb14SMatt Johnston &fd->specific.apply.progress_percent);
11648c2bfb14SMatt Johnston if (pending) {
11658c2bfb14SMatt Johnston if (res == PLDM_FWUP_APPLY_SUCCESS) {
11668c2bfb14SMatt Johnston /* Return without a ApplyComplete request.
11678c2bfb14SMatt Johnston * Will call apply() again on next call */
11688c2bfb14SMatt Johnston *req_payload_len = 0;
11698c2bfb14SMatt Johnston return 0;
11708c2bfb14SMatt Johnston }
11718c2bfb14SMatt Johnston /* This is an API infraction by the implementer, return a distinctive failure */
11728c2bfb14SMatt Johnston res = PLDM_FWUP_VENDOR_APPLY_RESULT_RANGE_MAX;
11738c2bfb14SMatt Johnston }
11748c2bfb14SMatt Johnston fd->req.result = res;
11758c2bfb14SMatt Johnston fd->req.complete = true;
11768c2bfb14SMatt Johnston if (fd->req.result ==
11778c2bfb14SMatt Johnston PLDM_FWUP_APPLY_SUCCESS_WITH_ACTIVATION_METHOD) {
11788c2bfb14SMatt Johnston /* modified activation method isn't currently handled */
11798c2bfb14SMatt Johnston fd->req.result = PLDM_FWUP_APPLY_SUCCESS;
11808c2bfb14SMatt Johnston }
11818c2bfb14SMatt Johnston }
11828c2bfb14SMatt Johnston
11838c2bfb14SMatt Johnston instance_id = pldm_fd_req_next_instance(&fd->req);
11848c2bfb14SMatt Johnston const struct pldm_apply_complete_req req_data = {
11858c2bfb14SMatt Johnston .apply_result = fd->req.result,
11868c2bfb14SMatt Johnston .comp_activation_methods_modification = { 0 },
11878c2bfb14SMatt Johnston };
11888c2bfb14SMatt Johnston rc = encode_apply_complete_req(instance_id, &req_data, req,
11898c2bfb14SMatt Johnston req_payload_len);
11908c2bfb14SMatt Johnston if (rc) {
11918c2bfb14SMatt Johnston return rc;
11928c2bfb14SMatt Johnston }
11938c2bfb14SMatt Johnston
11948c2bfb14SMatt Johnston /* Wait for response */
11958c2bfb14SMatt Johnston fd->req.state = PLDM_FD_REQ_SENT;
11968c2bfb14SMatt Johnston fd->req.instance_id = req->hdr.instance_id;
11978c2bfb14SMatt Johnston fd->req.command = req->hdr.command;
11988c2bfb14SMatt Johnston fd->req.sent_time = pldm_fd_now(fd);
11998c2bfb14SMatt Johnston
12008c2bfb14SMatt Johnston return 0;
12018c2bfb14SMatt Johnston }
12028c2bfb14SMatt Johnston
12038c2bfb14SMatt Johnston LIBPLDM_ABI_TESTING
pldm_fd_new(const struct pldm_fd_ops * ops,void * ops_ctx,struct pldm_control * control)1204cc6f6434SMatt Johnston struct pldm_fd *pldm_fd_new(const struct pldm_fd_ops *ops, void *ops_ctx,
1205cc6f6434SMatt Johnston struct pldm_control *control)
12068c2bfb14SMatt Johnston {
12078c2bfb14SMatt Johnston struct pldm_fd *fd = malloc(sizeof(*fd));
12088c2bfb14SMatt Johnston if (fd) {
1209cc6f6434SMatt Johnston if (pldm_fd_setup(fd, sizeof(*fd), ops, ops_ctx, control) ==
1210cc6f6434SMatt Johnston 0) {
12118c2bfb14SMatt Johnston return fd;
12128c2bfb14SMatt Johnston }
12138c2bfb14SMatt Johnston free(fd);
12148c2bfb14SMatt Johnston fd = NULL;
12158c2bfb14SMatt Johnston }
12168c2bfb14SMatt Johnston return fd;
12178c2bfb14SMatt Johnston }
12188c2bfb14SMatt Johnston
12198c2bfb14SMatt Johnston 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)12208c2bfb14SMatt Johnston int pldm_fd_setup(struct pldm_fd *fd, size_t pldm_fd_size,
1221cc6f6434SMatt Johnston const struct pldm_fd_ops *ops, void *ops_ctx,
1222cc6f6434SMatt Johnston struct pldm_control *control)
12238c2bfb14SMatt Johnston {
1224cc6f6434SMatt Johnston int rc;
1225cc6f6434SMatt Johnston
12268c2bfb14SMatt Johnston if (fd == NULL || ops == NULL) {
12278c2bfb14SMatt Johnston return -EINVAL;
12288c2bfb14SMatt Johnston }
12298c2bfb14SMatt Johnston
12308c2bfb14SMatt Johnston if (ops->device_identifiers == NULL || ops->components == NULL ||
12318c2bfb14SMatt Johnston ops->imageset_versions == NULL || ops->update_component == NULL ||
12328c2bfb14SMatt Johnston ops->transfer_size == NULL || ops->firmware_data == NULL ||
12338c2bfb14SMatt Johnston ops->verify == NULL || ops->activate == NULL ||
12348c2bfb14SMatt Johnston ops->cancel_update_component == NULL || ops->now == NULL) {
12358c2bfb14SMatt Johnston return -EINVAL;
12368c2bfb14SMatt Johnston }
12378c2bfb14SMatt Johnston
12388c2bfb14SMatt Johnston if (pldm_fd_size < sizeof(struct pldm_fd)) {
12398c2bfb14SMatt Johnston /* Safety check that sufficient storage was provided for *fd,
12408c2bfb14SMatt Johnston * in case PLDM_SIZEOF_PLDM_FD is incorrect */
12418c2bfb14SMatt Johnston return -EINVAL;
12428c2bfb14SMatt Johnston }
12438c2bfb14SMatt Johnston memset(fd, 0x0, sizeof(*fd));
12448c2bfb14SMatt Johnston fd->ops = ops;
12458c2bfb14SMatt Johnston fd->ops_ctx = ops_ctx;
12468c2bfb14SMatt Johnston fd->fd_t1_timeout = DEFAULT_FD_T1_TIMEOUT;
12478c2bfb14SMatt Johnston fd->fd_t2_retry_time = DEFAULT_FD_T2_RETRY_TIME;
12488c2bfb14SMatt Johnston
1249cc6f6434SMatt Johnston if (control) {
1250cc6f6434SMatt Johnston rc = pldm_control_add_type(control, PLDM_FWUP,
1251cc6f6434SMatt Johnston &PLDM_FD_VERSIONS,
1252cc6f6434SMatt Johnston PLDM_FD_VERSIONS_COUNT,
1253cc6f6434SMatt Johnston PLDM_FD_COMMANDS);
1254cc6f6434SMatt Johnston if (rc) {
1255cc6f6434SMatt Johnston return rc;
1256cc6f6434SMatt Johnston }
1257cc6f6434SMatt Johnston }
1258cc6f6434SMatt Johnston
12598c2bfb14SMatt Johnston return 0;
12608c2bfb14SMatt Johnston }
12618c2bfb14SMatt Johnston
12628c2bfb14SMatt Johnston 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)12638c2bfb14SMatt Johnston int pldm_fd_handle_msg(struct pldm_fd *fd, pldm_tid_t remote_address,
12648c2bfb14SMatt Johnston const void *in_msg, size_t in_len, void *out_msg,
12658c2bfb14SMatt Johnston size_t *out_len)
12668c2bfb14SMatt Johnston {
12678c2bfb14SMatt Johnston size_t req_payload_len;
12688c2bfb14SMatt Johnston size_t resp_payload_len;
12698c2bfb14SMatt Johnston struct pldm_header_info hdr;
12708c2bfb14SMatt Johnston const struct pldm_msg *req = in_msg;
12718c2bfb14SMatt Johnston struct pldm_msg *resp = out_msg;
12728c2bfb14SMatt Johnston uint8_t rc;
12738c2bfb14SMatt Johnston
12748c2bfb14SMatt Johnston if (fd == NULL || in_msg == NULL || out_msg == NULL ||
12758c2bfb14SMatt Johnston out_len == NULL) {
12768c2bfb14SMatt Johnston return -EINVAL;
12778c2bfb14SMatt Johnston }
12788c2bfb14SMatt Johnston
12798c2bfb14SMatt Johnston if (in_len < sizeof(struct pldm_msg_hdr)) {
12808c2bfb14SMatt Johnston return -EOVERFLOW;
12818c2bfb14SMatt Johnston }
12828c2bfb14SMatt Johnston req_payload_len = in_len - sizeof(struct pldm_msg_hdr);
12838c2bfb14SMatt Johnston
12848c2bfb14SMatt Johnston rc = unpack_pldm_header(&req->hdr, &hdr);
12858c2bfb14SMatt Johnston if (rc != PLDM_SUCCESS) {
12868c2bfb14SMatt Johnston return -EINVAL;
12878c2bfb14SMatt Johnston }
12888c2bfb14SMatt Johnston
12898c2bfb14SMatt Johnston if (hdr.pldm_type != PLDM_FWUP) {
12908c2bfb14SMatt Johnston /* Caller should not have passed non-pldmfw */
12918c2bfb14SMatt Johnston return -ENOMSG;
12928c2bfb14SMatt Johnston }
12938c2bfb14SMatt Johnston
12948c2bfb14SMatt Johnston if (hdr.msg_type == PLDM_RESPONSE) {
12958c2bfb14SMatt Johnston *out_len = 0;
12968c2bfb14SMatt Johnston return pldm_fd_handle_resp(fd, remote_address, in_msg, in_len);
12978c2bfb14SMatt Johnston }
12988c2bfb14SMatt Johnston
12998c2bfb14SMatt Johnston if (hdr.msg_type != PLDM_REQUEST) {
13008c2bfb14SMatt Johnston return -EPROTO;
13018c2bfb14SMatt Johnston }
13028c2bfb14SMatt Johnston
13038c2bfb14SMatt Johnston /* Space for header plus completion code */
13048c2bfb14SMatt Johnston if (*out_len < sizeof(struct pldm_msg_hdr) + 1) {
13058c2bfb14SMatt Johnston return -EOVERFLOW;
13068c2bfb14SMatt Johnston }
13078c2bfb14SMatt Johnston resp_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
13088c2bfb14SMatt Johnston
13098c2bfb14SMatt Johnston /* Check address */
13108c2bfb14SMatt Johnston switch (hdr.command) {
13118c2bfb14SMatt Johnston /* Information or cancel commands are always allowed */
13128c2bfb14SMatt Johnston case PLDM_QUERY_DEVICE_IDENTIFIERS:
13138c2bfb14SMatt Johnston case PLDM_GET_FIRMWARE_PARAMETERS:
13148c2bfb14SMatt Johnston case PLDM_GET_STATUS:
13158c2bfb14SMatt Johnston case PLDM_CANCEL_UPDATE:
13168c2bfb14SMatt Johnston case PLDM_QUERY_DOWNSTREAM_DEVICES:
13178c2bfb14SMatt Johnston case PLDM_QUERY_DOWNSTREAM_IDENTIFIERS:
13188c2bfb14SMatt Johnston case PLDM_QUERY_DOWNSTREAM_FIRMWARE_PARAMETERS:
13198c2bfb14SMatt Johnston /* Request Update handler will set address */
13208c2bfb14SMatt Johnston case PLDM_REQUEST_UPDATE:
13218c2bfb14SMatt Johnston break;
13228c2bfb14SMatt Johnston default:
13238c2bfb14SMatt Johnston /* Requests must come from the same address that requested the update */
13248c2bfb14SMatt Johnston if (!fd->ua_address_set || remote_address != fd->ua_address) {
13258c2bfb14SMatt Johnston return pldm_fd_reply_cc(PLDM_ERROR_NOT_READY, &hdr,
13268c2bfb14SMatt Johnston resp, &resp_payload_len);
13278c2bfb14SMatt Johnston }
13288c2bfb14SMatt Johnston }
13298c2bfb14SMatt Johnston
13308c2bfb14SMatt Johnston /* Update timeout */
13318c2bfb14SMatt Johnston switch (hdr.command) {
13328c2bfb14SMatt Johnston case PLDM_REQUEST_UPDATE:
13338c2bfb14SMatt Johnston case PLDM_PASS_COMPONENT_TABLE:
13348c2bfb14SMatt Johnston case PLDM_UPDATE_COMPONENT:
13358c2bfb14SMatt Johnston case PLDM_CANCEL_UPDATE:
13368c2bfb14SMatt Johnston fd->update_timestamp_fd_t1 = pldm_fd_now(fd);
13378c2bfb14SMatt Johnston break;
13388c2bfb14SMatt Johnston default:
13398c2bfb14SMatt Johnston break;
13408c2bfb14SMatt Johnston }
13418c2bfb14SMatt Johnston
1342cc6f6434SMatt Johnston /* Dispatch command.
1343cc6f6434SMatt Johnston Update PLDM_FD_COMMANDS if adding new handlers */
13448c2bfb14SMatt Johnston switch (hdr.command) {
13458c2bfb14SMatt Johnston case PLDM_QUERY_DEVICE_IDENTIFIERS:
13468c2bfb14SMatt Johnston rc = pldm_fd_qdi(fd, &hdr, req, req_payload_len, resp,
13478c2bfb14SMatt Johnston &resp_payload_len);
13488c2bfb14SMatt Johnston break;
13498c2bfb14SMatt Johnston case PLDM_GET_FIRMWARE_PARAMETERS:
13508c2bfb14SMatt Johnston rc = pldm_fd_fw_param(fd, &hdr, req, req_payload_len, resp,
13518c2bfb14SMatt Johnston &resp_payload_len);
13528c2bfb14SMatt Johnston break;
13538c2bfb14SMatt Johnston case PLDM_REQUEST_UPDATE:
13548c2bfb14SMatt Johnston rc = pldm_fd_request_update(fd, &hdr, req, req_payload_len,
13558c2bfb14SMatt Johnston resp, &resp_payload_len,
13568c2bfb14SMatt Johnston remote_address);
13578c2bfb14SMatt Johnston break;
13588c2bfb14SMatt Johnston case PLDM_PASS_COMPONENT_TABLE:
13598c2bfb14SMatt Johnston rc = pldm_fd_pass_comp(fd, &hdr, req, req_payload_len, resp,
13608c2bfb14SMatt Johnston &resp_payload_len);
13618c2bfb14SMatt Johnston break;
13628c2bfb14SMatt Johnston case PLDM_UPDATE_COMPONENT:
13638c2bfb14SMatt Johnston rc = pldm_fd_update_comp(fd, &hdr, req, req_payload_len, resp,
13648c2bfb14SMatt Johnston &resp_payload_len);
13658c2bfb14SMatt Johnston break;
13668c2bfb14SMatt Johnston case PLDM_GET_STATUS:
13678c2bfb14SMatt Johnston rc = pldm_fd_get_status(fd, &hdr, req, req_payload_len, resp,
13688c2bfb14SMatt Johnston &resp_payload_len);
13698c2bfb14SMatt Johnston break;
13708c2bfb14SMatt Johnston case PLDM_CANCEL_UPDATE_COMPONENT:
13718c2bfb14SMatt Johnston rc = pldm_fd_cancel_update_comp(fd, &hdr, req, req_payload_len,
13728c2bfb14SMatt Johnston resp, &resp_payload_len);
13738c2bfb14SMatt Johnston break;
13748c2bfb14SMatt Johnston case PLDM_CANCEL_UPDATE:
13758c2bfb14SMatt Johnston rc = pldm_fd_cancel_update(fd, &hdr, req, req_payload_len, resp,
13768c2bfb14SMatt Johnston &resp_payload_len);
13778c2bfb14SMatt Johnston break;
13788c2bfb14SMatt Johnston case PLDM_ACTIVATE_FIRMWARE:
13798c2bfb14SMatt Johnston rc = pldm_fd_activate_firmware(fd, &hdr, req, req_payload_len,
13808c2bfb14SMatt Johnston resp, &resp_payload_len);
13818c2bfb14SMatt Johnston break;
13828c2bfb14SMatt Johnston default:
13838c2bfb14SMatt Johnston rc = pldm_fd_reply_cc(PLDM_ERROR_UNSUPPORTED_PLDM_CMD, &hdr,
13848c2bfb14SMatt Johnston resp, &resp_payload_len);
13858c2bfb14SMatt Johnston }
13868c2bfb14SMatt Johnston
13878c2bfb14SMatt Johnston if (rc == 0) {
13888c2bfb14SMatt Johnston *out_len = resp_payload_len + sizeof(struct pldm_msg_hdr);
13898c2bfb14SMatt Johnston }
13908c2bfb14SMatt Johnston
13918c2bfb14SMatt Johnston return rc;
13928c2bfb14SMatt Johnston }
13938c2bfb14SMatt Johnston
13948c2bfb14SMatt Johnston LIBPLDM_ABI_TESTING
pldm_fd_progress(struct pldm_fd * fd,void * out_msg,size_t * out_len,pldm_tid_t * address)13958c2bfb14SMatt Johnston int pldm_fd_progress(struct pldm_fd *fd, void *out_msg, size_t *out_len,
13968c2bfb14SMatt Johnston pldm_tid_t *address)
13978c2bfb14SMatt Johnston {
13988c2bfb14SMatt Johnston size_t req_payload_len;
13998c2bfb14SMatt Johnston struct pldm_msg *req = out_msg;
14008c2bfb14SMatt Johnston int rc = -EINVAL;
1401*2628990bSMatt Johnston bool ua_timeout_check = false;
14028c2bfb14SMatt Johnston
14038c2bfb14SMatt Johnston if (fd == NULL || out_msg == NULL || out_len == NULL) {
14048c2bfb14SMatt Johnston return -EINVAL;
14058c2bfb14SMatt Johnston }
14068c2bfb14SMatt Johnston
14078c2bfb14SMatt Johnston /* Space for header */
14088c2bfb14SMatt Johnston if (*out_len < sizeof(struct pldm_msg_hdr)) {
14098c2bfb14SMatt Johnston return -EOVERFLOW;
14108c2bfb14SMatt Johnston }
14118c2bfb14SMatt Johnston req_payload_len = *out_len - sizeof(struct pldm_msg_hdr);
14128c2bfb14SMatt Johnston *out_len = 0;
14138c2bfb14SMatt Johnston
1414*2628990bSMatt Johnston // Handle FD-driven states
14158c2bfb14SMatt Johnston switch (fd->state) {
14168c2bfb14SMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
14178c2bfb14SMatt Johnston rc = pldm_fd_progress_download(fd, req, &req_payload_len);
14188c2bfb14SMatt Johnston break;
14198c2bfb14SMatt Johnston case PLDM_FD_STATE_VERIFY:
14208c2bfb14SMatt Johnston rc = pldm_fd_progress_verify(fd, req, &req_payload_len);
14218c2bfb14SMatt Johnston break;
14228c2bfb14SMatt Johnston case PLDM_FD_STATE_APPLY:
14238c2bfb14SMatt Johnston rc = pldm_fd_progress_apply(fd, req, &req_payload_len);
14248c2bfb14SMatt Johnston break;
14258c2bfb14SMatt Johnston default:
14268c2bfb14SMatt Johnston req_payload_len = 0;
1427*2628990bSMatt Johnston break;
1428*2628990bSMatt Johnston }
1429*2628990bSMatt Johnston
1430*2628990bSMatt Johnston // Cancel update if expected UA message isn't received within timeout
1431*2628990bSMatt Johnston switch (fd->state) {
1432*2628990bSMatt Johnston case PLDM_FD_STATE_DOWNLOAD:
1433*2628990bSMatt Johnston case PLDM_FD_STATE_VERIFY:
1434*2628990bSMatt Johnston case PLDM_FD_STATE_APPLY:
1435*2628990bSMatt Johnston // FD-driven states will time out if a response isn't received
1436*2628990bSMatt Johnston ua_timeout_check = (fd->req.state == PLDM_FD_REQ_SENT);
1437*2628990bSMatt Johnston break;
1438*2628990bSMatt Johnston case PLDM_FD_STATE_IDLE:
1439*2628990bSMatt Johnston ua_timeout_check = false;
1440*2628990bSMatt Johnston break;
1441*2628990bSMatt Johnston default:
1442*2628990bSMatt Johnston // Other Update Mode states have a timeout for the UA to
1443*2628990bSMatt Johnston // send a request
1444*2628990bSMatt Johnston ua_timeout_check = true;
1445*2628990bSMatt Johnston }
1446*2628990bSMatt Johnston
1447*2628990bSMatt Johnston if (ua_timeout_check) {
14488c2bfb14SMatt Johnston if ((pldm_fd_now(fd) - fd->update_timestamp_fd_t1) >
14498c2bfb14SMatt Johnston fd->fd_t1_timeout) {
14508c2bfb14SMatt Johnston pldm_fd_maybe_cancel_component(fd);
14518c2bfb14SMatt Johnston pldm_fd_idle_timeout(fd);
1452*2628990bSMatt Johnston req_payload_len = 0;
14538c2bfb14SMatt Johnston }
14548c2bfb14SMatt Johnston }
14558c2bfb14SMatt Johnston
14568c2bfb14SMatt Johnston if (rc == 0 && fd->ua_address_set && req_payload_len > 0) {
14578c2bfb14SMatt Johnston *out_len = req_payload_len + sizeof(struct pldm_msg_hdr);
14588c2bfb14SMatt Johnston *address = fd->ua_address;
14598c2bfb14SMatt Johnston }
14608c2bfb14SMatt Johnston
14618c2bfb14SMatt Johnston return rc;
14628c2bfb14SMatt Johnston }
14638c2bfb14SMatt Johnston
14648c2bfb14SMatt Johnston LIBPLDM_ABI_TESTING
pldm_fd_set_update_idle_timeout(struct pldm_fd * fd,uint32_t time)14658c2bfb14SMatt Johnston int pldm_fd_set_update_idle_timeout(struct pldm_fd *fd, uint32_t time)
14668c2bfb14SMatt Johnston {
14678c2bfb14SMatt Johnston if (fd == NULL) {
14688c2bfb14SMatt Johnston return -EINVAL;
14698c2bfb14SMatt Johnston }
14708c2bfb14SMatt Johnston
14718c2bfb14SMatt Johnston fd->fd_t1_timeout = time;
14728c2bfb14SMatt Johnston return 0;
14738c2bfb14SMatt Johnston }
14748c2bfb14SMatt Johnston
14758c2bfb14SMatt Johnston LIBPLDM_ABI_TESTING
pldm_fd_set_request_retry_time(struct pldm_fd * fd,uint32_t time)14768c2bfb14SMatt Johnston int pldm_fd_set_request_retry_time(struct pldm_fd *fd, uint32_t time)
14778c2bfb14SMatt Johnston {
14788c2bfb14SMatt Johnston if (fd == NULL) {
14798c2bfb14SMatt Johnston return -EINVAL;
14808c2bfb14SMatt Johnston }
14818c2bfb14SMatt Johnston
14828c2bfb14SMatt Johnston fd->fd_t2_retry_time = time;
14838c2bfb14SMatt Johnston return 0;
14848c2bfb14SMatt Johnston }
1485