in core/spdm/spdm_commands.c [1692:1858]
int spdm_get_capabilities (const struct cmd_interface_spdm_responder *spdm_responder,
struct cmd_interface_msg *request)
{
int status;
int spdm_error;
struct spdm_protocol_header *header;
struct spdm_get_capabilities *req_resp;
uint8_t spdm_version;
size_t req_resp_size;
const struct spdm_transcript_manager *transcript_manager;
struct spdm_state *state;
const struct spdm_device_capability *local_capabilities;
uint16_t minor_ver_in_error_msg;
if ((spdm_responder == NULL) || (request == NULL)) {
return CMD_HANDLER_SPDM_RESPONDER_INVALID_ARGUMENT;
}
transcript_manager = spdm_responder->transcript_manager;
state = spdm_responder->state;
local_capabilities = spdm_responder->local_capabilities;
/* Validate request version and save it in the connection info. */
header = (struct spdm_protocol_header*) request->payload;
spdm_version = SPDM_MAKE_VERSION (header->spdm_major_version, header->spdm_minor_version);
if (spdm_check_request_version_compatibility (state, spdm_responder->version_num,
spdm_responder->version_num_count, spdm_version) == false) {
minor_ver_in_error_msg = 0;
status = CMD_HANDLER_SPDM_RESPONDER_VERSION_MISMATCH;
spdm_error = SPDM_ERROR_VERSION_MISMATCH;
goto exit;
}
minor_ver_in_error_msg = state->connection_info.version.minor_version;
/* Verify the state. */
if (state->response_state != SPDM_RESPONSE_STATE_NORMAL) {
spdm_handle_response_state (state, &spdm_error);
status = CMD_HANDLER_SPDM_RESPONDER_INTERNAL_ERROR;
goto exit;
}
if (state->connection_info.connection_state != SPDM_CONNECTION_STATE_AFTER_VERSION) {
status = CMD_HANDLER_SPDM_RESPONDER_UNEXPECTED_REQUEST;
spdm_error = SPDM_ERROR_UNEXPECTED_REQUEST;
goto exit;
}
/* Check request size. */
if ((spdm_version >= SPDM_VERSION_1_2) &&
(request->payload_length >= sizeof (struct spdm_get_capabilities))) {
req_resp_size = sizeof (struct spdm_get_capabilities);
}
else if ((spdm_version == SPDM_VERSION_1_1) &&
(request->payload_length >= sizeof (struct spdm_get_capabilities_1_1))) {
req_resp_size = sizeof (struct spdm_get_capabilities_1_1);
}
else {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
/* Process the request. */
req_resp = (struct spdm_get_capabilities*) request->payload;
/* Check for request flag compatibility. */
if (spdm_check_request_flag_compatibility (req_resp->base_capabilities.flags, spdm_version) ==
false) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
/* Check the data transfer size. */
if (spdm_version >= SPDM_VERSION_1_2) {
if ((req_resp->data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_1_2) ||
(req_resp->data_transfer_size > req_resp->max_spdm_msg_size)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
if ((req_resp->base_capabilities.flags.chunk_cap == 0) &&
(req_resp->data_transfer_size != req_resp->max_spdm_msg_size)) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
}
/* Check the CT Exponent. */
if (spdm_version >= SPDM_VERSION_1_1) {
if (req_resp->base_capabilities.ct_exponent > SPDM_MAX_CT_EXPONENT) {
status = CMD_HANDLER_SPDM_RESPONDER_INVALID_REQUEST;
spdm_error = SPDM_ERROR_INVALID_REQUEST;
goto exit;
}
}
/* Update SPDM version in transcript manager to make sure proper behavior */
transcript_manager->set_spdm_version (spdm_responder->transcript_manager, spdm_version);
/* Reset the transcript manager state as per the request code. */
spdm_reset_transcript_via_request_code (state, transcript_manager,
SPDM_REQUEST_GET_CAPABILITIES);
/* Append the request to the VCA buffer. */
status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_VCA,
(const uint8_t*) req_resp, req_resp_size, false, SPDM_MAX_SESSION_COUNT);
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
/* Save the requester capabilities in the connection info. */
state->connection_info.peer_capabilities.flags = req_resp->base_capabilities.flags;
state->connection_info.peer_capabilities.ct_exponent = req_resp->base_capabilities.ct_exponent;
if (spdm_version >= SPDM_VERSION_1_2) {
state->connection_info.peer_capabilities.data_transfer_size = req_resp->data_transfer_size;
state->connection_info.peer_capabilities.max_spdm_msg_size = req_resp->max_spdm_msg_size;
}
else {
state->connection_info.peer_capabilities.data_transfer_size = 0;
state->connection_info.peer_capabilities.max_spdm_msg_size = 0;
}
/* Response phase. */
/* Contruct the response. */
memset (req_resp, 0, req_resp_size);
spdm_populate_header (&req_resp->base_capabilities.header, SPDM_RESPONSE_GET_CAPABILITIES,
SPDM_GET_MINOR_VERSION (spdm_version));
req_resp->base_capabilities.reserved = 0;
req_resp->base_capabilities.reserved2 = 0;
req_resp->base_capabilities.reserved3 = 0;
req_resp->base_capabilities.reserved4 = 0;
req_resp->base_capabilities.ct_exponent = local_capabilities->ct_exponent;
req_resp->base_capabilities.flags = local_capabilities->flags;
if (spdm_version >= SPDM_VERSION_1_2) {
req_resp->data_transfer_size = local_capabilities->data_transfer_size;
req_resp->max_spdm_msg_size = local_capabilities->max_spdm_msg_size;
}
/* Append the reponse to the VCA buffer. */
status = transcript_manager->update (transcript_manager, TRANSCRIPT_CONTEXT_TYPE_VCA,
(const uint8_t*) req_resp, req_resp_size, false, SPDM_MAX_SESSION_COUNT);
if (status != 0) {
spdm_error = SPDM_ERROR_UNSPECIFIED;
goto exit;
}
/* Set the payload length. */
cmd_interface_msg_set_message_payload_length (request, req_resp_size);
/* Update connection state */
spdm_set_connection_state (state, SPDM_CONNECTION_STATE_AFTER_CAPABILITIES);
exit:
if (status != 0) {
spdm_generate_error_response (request, minor_ver_in_error_msg, spdm_error, 0x00, NULL, 0,
SPDM_REQUEST_GET_CAPABILITIES, status);
}
return 0;
}