int spdm_get_capabilities()

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;
}