#include #include #include #include #include #include #include #include #include #include "control-internal.h" #define PLDM_BASE_VERSIONS_COUNT 2 static const uint32_t PLDM_BASE_VERSIONS[PLDM_BASE_VERSIONS_COUNT] = { /* PLDM 1.1.0 is current implemented. */ 0xf1f1f000, /* CRC. Calculated with python: hex(crccheck.crc.Crc32.calc(struct.pack('instance, PLDM_FWUP, req_hdr->command, ccode, resp); if (rc != PLDM_SUCCESS) { return -EINVAL; } return 0; } static int pldm_control_get_tid(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) { if (req_payload_len != PLDM_GET_TID_REQ_BYTES) { return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr, resp, resp_payload_len); } if (*resp_payload_len <= PLDM_GET_TID_RESP_BYTES) { return -EOVERFLOW; } *resp_payload_len = PLDM_GET_TID_RESP_BYTES; uint8_t cc = encode_get_tid_resp(hdr->instance, PLDM_SUCCESS, PLDM_TID_UNASSIGNED, resp); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } return 0; } static int pldm_control_get_version(struct pldm_control *control, 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 cc; uint32_t handle; uint8_t opflag; uint8_t type; cc = decode_get_version_req(req, req_payload_len, &handle, &opflag, &type); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } /* Response is always sent as a single transfer */ if (opflag != PLDM_GET_FIRSTPART) { return pldm_control_reply_error( PLDM_CONTROL_INVALID_TRANSFER_OPERATION_FLAG, hdr, resp, resp_payload_len); } const struct pldm_type_versions *v = NULL; for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) { if (control->types[i].pldm_type == type && control->types[i].versions) { v = &control->types[i]; break; } } if (!v) { return pldm_control_reply_error( PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr, resp, resp_payload_len); } /* encode_get_version_resp doesn't have length checking */ uint32_t required_resp_payload = 1 + 4 + 1 + v->versions_count * sizeof(ver32_t); if (*resp_payload_len < required_resp_payload) { return -EOVERFLOW; } *resp_payload_len = required_resp_payload; /* crc32 is included in the versions buffer */ cc = encode_get_version_resp(hdr->instance, PLDM_SUCCESS, 0, PLDM_START_AND_END, v->versions, v->versions_count * sizeof(ver32_t), resp); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } return 0; } static int pldm_control_get_types(struct pldm_control *control, 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) { uint8_t cc; if (req_payload_len != PLDM_GET_TYPES_REQ_BYTES) { return pldm_control_reply_error(PLDM_ERROR_INVALID_LENGTH, hdr, resp, resp_payload_len); } bitfield8_t types[8]; memset(types, 0, sizeof(types)); for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) { uint8_t ty = control->types[i].pldm_type; if (ty < 64 && control->types[i].versions) { uint8_t bit = 1 << (ty % 8); types[ty / 8].byte |= bit; } } /* encode_get_types_resp doesn't have length checking */ uint32_t required_resp_payload = 1 + 8; if (*resp_payload_len < required_resp_payload) { return -EOVERFLOW; } *resp_payload_len = required_resp_payload; cc = encode_get_types_resp(hdr->instance, PLDM_SUCCESS, types, resp); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } return 0; } static int pldm_control_get_commands(struct pldm_control *control, 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 cc; uint8_t ty; // version in request is ignored, since SelectPLDMVersion isn't supported currently ver32_t version; cc = decode_get_commands_req(req, req_payload_len, &ty, &version); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } const struct pldm_type_versions *v = NULL; for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) { if (control->types[i].pldm_type == ty && control->types[i].versions && control->types[i].commands) { v = &control->types[i]; break; } } if (!v) { return pldm_control_reply_error( PLDM_CONTROL_INVALID_PLDM_TYPE_IN_REQUEST_DATA, hdr, resp, resp_payload_len); } /* encode_get_commands_resp doesn't have length checking */ uint32_t required_resp_payload = 1 + 32; if (*resp_payload_len < required_resp_payload) { return -EOVERFLOW; } *resp_payload_len = required_resp_payload; cc = encode_get_commands_resp(hdr->instance, PLDM_SUCCESS, v->commands, resp); if (cc) { return pldm_control_reply_error(cc, hdr, resp, resp_payload_len); } return 0; } /* A response should only be used when this returns 0, and *resp_len > 0 */ LIBPLDM_ABI_TESTING int pldm_control_handle_msg(struct pldm_control *control, const void *req_msg, size_t req_len, void *resp_msg, size_t *resp_len) { int rc; /* Space for header plus completion code */ if (*resp_len < sizeof(struct pldm_msg_hdr) + 1) { return -EOVERFLOW; } size_t resp_payload_len = *resp_len - sizeof(struct pldm_msg_hdr); struct pldm_msg *resp = resp_msg; if (req_len < sizeof(struct pldm_msg_hdr)) { return -EOVERFLOW; } size_t req_payload_len = req_len - sizeof(struct pldm_msg_hdr); const struct pldm_msg *req = req_msg; struct pldm_header_info hdr; rc = unpack_pldm_header(&req->hdr, &hdr); if (rc != PLDM_SUCCESS) { return -EINVAL; } if (hdr.pldm_type != PLDM_BASE) { /* Caller should not have passed non-control */ return -ENOMSG; } if (hdr.msg_type != PLDM_REQUEST) { return -EINVAL; } /* Dispatch command */ switch (hdr.command) { case PLDM_GET_TID: rc = pldm_control_get_tid(&hdr, req, req_payload_len, resp, &resp_payload_len); break; case PLDM_GET_PLDM_VERSION: rc = pldm_control_get_version(control, &hdr, req, req_payload_len, resp, &resp_payload_len); break; case PLDM_GET_PLDM_TYPES: rc = pldm_control_get_types(control, &hdr, req, req_payload_len, resp, &resp_payload_len); break; case PLDM_GET_PLDM_COMMANDS: rc = pldm_control_get_commands(control, &hdr, req, req_payload_len, resp, &resp_payload_len); break; default: rc = pldm_control_reply_error(PLDM_ERROR_UNSUPPORTED_PLDM_CMD, &hdr, resp, &resp_payload_len); } if (rc == 0) { *resp_len = resp_payload_len + sizeof(struct pldm_msg_hdr); } return rc; } LIBPLDM_ABI_TESTING int pldm_control_setup(struct pldm_control *control, size_t pldm_control_size) { int rc; if (pldm_control_size < sizeof(struct pldm_control)) { return -EINVAL; } memset(control, 0, sizeof(struct pldm_control)); rc = pldm_control_add_type(control, PLDM_BASE, &PLDM_BASE_VERSIONS, PLDM_BASE_VERSIONS_COUNT, PLDM_CONTROL_COMMANDS); if (rc) { return rc; } return 0; } LIBPLDM_ABI_TESTING int pldm_control_add_type(struct pldm_control *control, uint8_t pldm_type, const void *versions, size_t versions_count, const bitfield8_t *commands) { if (versions_count < 2) { /* At least one version must be provided, along with a CRC32 */ return -EINVAL; } struct pldm_type_versions *v = NULL; for (int i = 0; i < PLDM_CONTROL_MAX_VERSION_TYPES; i++) { if (control->types[i].versions == NULL || (control->types[i].versions != NULL && control->types[i].pldm_type == pldm_type)) { v = &control->types[i]; break; } } if (!v) { return -ENOMEM; // No spare slots } v->pldm_type = pldm_type; v->versions = versions; v->versions_count = versions_count; v->commands = commands; return 0; }