Line data Source code
1 : /**
2 : * Copyright Notice:
3 : * Copyright 2021-2025 DMTF. All rights reserved.
4 : * License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
5 : **/
6 :
7 : #include "internal/libspdm_responder_lib.h"
8 :
9 : #if LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP
10 :
11 23 : libspdm_return_t libspdm_get_response_chunk_send(libspdm_context_t *spdm_context,
12 : size_t request_size,
13 : const void *request,
14 : size_t *response_size,
15 : void *response)
16 : {
17 : const spdm_chunk_send_request_t *spdm_request;
18 : spdm_chunk_send_ack_response_t *spdm_response;
19 : libspdm_chunk_info_t *send_info;
20 23 : libspdm_return_t status = LIBSPDM_STATUS_SUCCESS;
21 : const uint8_t *chunk;
22 : uint32_t large_message_size;
23 : uint32_t calc_max_chunk_size;
24 : uint8_t *scratch_buffer;
25 : size_t scratch_buffer_size;
26 : uint8_t* chunk_response;
27 : size_t chunk_response_size;
28 : uint64_t max_chunk_data_transfer_size;
29 :
30 23 : spdm_request = (const spdm_chunk_send_request_t*) request;
31 :
32 23 : if (libspdm_get_connection_version(spdm_context) < SPDM_MESSAGE_VERSION_12) {
33 0 : return libspdm_generate_error_response(spdm_context,
34 : SPDM_ERROR_CODE_UNSUPPORTED_REQUEST,
35 : SPDM_CHUNK_SEND,
36 : response_size, response);
37 : }
38 :
39 23 : if (!libspdm_is_capabilities_flag_supported(
40 : spdm_context, false, SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CHUNK_CAP,
41 : SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CHUNK_CAP)) {
42 1 : return libspdm_generate_error_response(
43 : spdm_context,
44 : SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
45 : response_size, response);
46 : }
47 :
48 : /*chunk mechanism can be used for normal or encap state*/
49 22 : if ((spdm_context->response_state != LIBSPDM_RESPONSE_STATE_NORMAL) &&
50 1 : (spdm_context->response_state != LIBSPDM_RESPONSE_STATE_PROCESSING_ENCAP)) {
51 1 : return libspdm_responder_handle_response_state(
52 : spdm_context,
53 1 : spdm_request->header.request_response_code,
54 : response_size, response);
55 : }
56 :
57 21 : if (spdm_context->connection_info.connection_state <
58 : LIBSPDM_CONNECTION_STATE_AFTER_CAPABILITIES) {
59 1 : libspdm_generate_error_response(
60 : spdm_context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
61 : response_size, response);
62 1 : return LIBSPDM_STATUS_SUCCESS;
63 : }
64 :
65 20 : if (request_size < sizeof(spdm_chunk_send_request_t)) {
66 1 : libspdm_generate_error_response(
67 : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
68 : response_size, response);
69 1 : return LIBSPDM_STATUS_SUCCESS;
70 : }
71 :
72 19 : if (spdm_request->header.spdm_version < SPDM_MESSAGE_VERSION_12) {
73 1 : return libspdm_generate_error_response(
74 : spdm_context, SPDM_ERROR_CODE_UNSUPPORTED_REQUEST, SPDM_CHUNK_SEND,
75 : response_size, response);
76 : }
77 :
78 18 : if (spdm_request->header.spdm_version
79 18 : != libspdm_get_connection_version(spdm_context)) {
80 1 : return libspdm_generate_error_response(
81 : spdm_context, SPDM_ERROR_CODE_VERSION_MISMATCH, 0,
82 : response_size, response);
83 : }
84 :
85 17 : if (spdm_context->chunk_context.get.chunk_in_use) {
86 : /* Specification does not support simultaneous chunk send and chunk get. */
87 1 : return libspdm_generate_error_response(
88 : spdm_context, SPDM_ERROR_CODE_UNEXPECTED_REQUEST, 0,
89 : response_size, response);
90 : }
91 :
92 16 : send_info = &spdm_context->chunk_context.send;
93 :
94 16 : if (!send_info->chunk_in_use) {
95 :
96 7 : if (request_size < sizeof(spdm_chunk_send_request_t) + sizeof(uint32_t)) {
97 0 : libspdm_generate_error_response(
98 : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
99 : response_size, response);
100 0 : return LIBSPDM_STATUS_SUCCESS;
101 : }
102 :
103 7 : large_message_size = *(const uint32_t*) (spdm_request + 1);
104 7 : chunk = (((const uint8_t*) (spdm_request + 1)) + sizeof(uint32_t));
105 7 : calc_max_chunk_size =
106 7 : (uint32_t)request_size - (sizeof(spdm_chunk_send_request_t) + sizeof(uint32_t));
107 7 : max_chunk_data_transfer_size =
108 7 : ((size_t) spdm_context->local_context.capability.data_transfer_size
109 7 : - sizeof(spdm_chunk_send_request_t)) * 65536 - sizeof(uint32_t);
110 :
111 7 : if (spdm_request->chunk_seq_no != 0
112 6 : || (spdm_request->chunk_size
113 : < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
114 : - sizeof(spdm_chunk_send_request_t)
115 : - sizeof(uint32_t))
116 6 : || spdm_request->chunk_size > calc_max_chunk_size
117 5 : || (uint32_t)request_size > spdm_context->local_context.capability.data_transfer_size
118 4 : || large_message_size > spdm_context->local_context.capability.max_spdm_msg_size
119 2 : || large_message_size > max_chunk_data_transfer_size
120 2 : || large_message_size <= SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
121 2 : || (spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
122 : ) {
123 6 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
124 : } else {
125 1 : libspdm_get_scratch_buffer(spdm_context, (void**) &scratch_buffer,
126 : &scratch_buffer_size);
127 :
128 1 : send_info->chunk_in_use = true;
129 1 : send_info->chunk_handle = spdm_request->header.param2;
130 1 : send_info->chunk_seq_no = spdm_request->chunk_seq_no;
131 :
132 2 : send_info->large_message = scratch_buffer +
133 1 : libspdm_get_scratch_buffer_large_message_offset(spdm_context);
134 1 : send_info->large_message_capacity =
135 1 : libspdm_get_scratch_buffer_large_message_capacity(spdm_context);
136 1 : send_info->large_message_size = large_message_size;
137 1 : send_info->chunk_bytes_transferred = spdm_request->chunk_size;
138 :
139 1 : libspdm_copy_mem(
140 : send_info->large_message, send_info->large_message_capacity,
141 1 : chunk, spdm_request->chunk_size);
142 : }
143 : } else {
144 :
145 9 : chunk = (const uint8_t*) (spdm_request + 1);
146 9 : calc_max_chunk_size =
147 9 : (uint32_t)request_size - sizeof(spdm_chunk_send_request_t);
148 :
149 9 : if (spdm_request->chunk_seq_no != send_info->chunk_seq_no + 1
150 4 : || spdm_request->header.param2 != send_info->chunk_handle
151 3 : || spdm_request->chunk_size > calc_max_chunk_size
152 3 : || spdm_request->chunk_size + send_info->chunk_bytes_transferred
153 3 : > send_info->large_message_size) {
154 8 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
155 1 : } else if ((spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
156 1 : && (spdm_request->chunk_size + send_info->chunk_bytes_transferred
157 1 : != send_info->large_message_size)) {
158 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
159 1 : } else if (!(spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK)
160 0 : && ((spdm_request->chunk_size + send_info->chunk_bytes_transferred
161 0 : > send_info->large_message_size)
162 0 : || (spdm_request->chunk_size
163 : < SPDM_MIN_DATA_TRANSFER_SIZE_VERSION_12
164 : - sizeof(spdm_chunk_send_request_t))
165 0 : || ((uint32_t) request_size
166 0 : > spdm_context->local_context.capability.data_transfer_size))) {
167 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
168 1 : } else if (spdm_request->chunk_seq_no == 0) {
169 : /* Chunk seq no wrapped */
170 0 : status = LIBSPDM_STATUS_INVALID_MSG_FIELD;
171 : } else {
172 :
173 1 : libspdm_copy_mem(
174 1 : (uint8_t*)send_info->large_message + send_info->chunk_bytes_transferred,
175 1 : send_info->large_message_size - send_info->chunk_bytes_transferred,
176 1 : chunk, spdm_request->chunk_size);
177 :
178 1 : send_info->chunk_seq_no = spdm_request->chunk_seq_no;
179 1 : send_info->chunk_bytes_transferred += spdm_request->chunk_size;
180 1 : if (spdm_request->header.param1 & SPDM_CHUNK_SEND_REQUEST_ATTRIBUTE_LAST_CHUNK) {
181 1 : send_info->chunk_in_use= false;
182 : }
183 : }
184 : }
185 :
186 16 : LIBSPDM_ASSERT(*response_size >= sizeof(spdm_chunk_send_ack_response_t));
187 :
188 16 : libspdm_zero_mem(response, *response_size);
189 16 : spdm_response = response;
190 :
191 16 : spdm_response->header.spdm_version = spdm_request->header.spdm_version;
192 16 : spdm_response->header.request_response_code = SPDM_CHUNK_SEND_ACK;
193 16 : spdm_response->header.param1 = 0;
194 16 : spdm_response->header.param2 = spdm_request->header.param2; /* handle */
195 16 : spdm_response->chunk_seq_no = spdm_request->chunk_seq_no;
196 :
197 16 : chunk_response = (uint8_t*) (spdm_response + 1);
198 16 : chunk_response_size = *response_size - sizeof(spdm_chunk_send_ack_response_t);
199 :
200 16 : if (LIBSPDM_STATUS_IS_ERROR(status)) {
201 : /* Set the EARLY_ERROR_DETECTED bit here, because one of the CHUNK_SEND requests failed.
202 : * If there is an error after all chunks have been sent by the requester correctly,
203 : * the responder reflects the error in the ChunkSendAck.ResponseToLargeRequest buffer,
204 : * and not in the EARLY_ERROR_DETECTED bit. */
205 :
206 14 : spdm_response->header.param1
207 14 : |= SPDM_CHUNK_SEND_ACK_RESPONSE_ATTRIBUTE_EARLY_ERROR_DETECTED;
208 :
209 14 : libspdm_generate_error_response(
210 : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
211 : &chunk_response_size, chunk_response);
212 :
213 14 : *response_size = sizeof(spdm_chunk_send_ack_response_t) + chunk_response_size;
214 :
215 14 : send_info->chunk_in_use = false;
216 14 : send_info->chunk_handle = 0;
217 14 : send_info->chunk_seq_no = 0;
218 14 : send_info->chunk_bytes_transferred = 0;
219 14 : send_info->large_message = NULL;
220 14 : send_info->large_message_size = 0;
221 2 : } else if (send_info->chunk_bytes_transferred == send_info->large_message_size) {
222 : uint8_t opcode;
223 :
224 1 : opcode = ((spdm_message_header_t*)send_info->large_message)->request_response_code;
225 : libspdm_get_spdm_response_func response_func =
226 1 : libspdm_get_response_func_via_request_code(opcode);
227 :
228 1 : if ((response_func != NULL) &&
229 1 : (opcode != SPDM_CHUNK_SEND) && (opcode != SPDM_CHUNK_GET)) {
230 1 : status = response_func(
231 : spdm_context,
232 1 : send_info->large_message_size, send_info->large_message,
233 : &chunk_response_size, chunk_response);
234 : } else {
235 0 : status = LIBSPDM_STATUS_SUCCESS;
236 0 : libspdm_generate_error_response(
237 : spdm_context, SPDM_ERROR_CODE_INVALID_REQUEST, 0,
238 : &chunk_response_size, chunk_response);
239 : }
240 :
241 1 : send_info->chunk_in_use = false;
242 1 : send_info->chunk_handle = 0;
243 1 : send_info->chunk_seq_no = 0;
244 1 : send_info->chunk_bytes_transferred = 0;
245 1 : send_info->large_message = NULL;
246 1 : send_info->large_message_size = 0;
247 :
248 1 : *response_size = sizeof(spdm_chunk_send_ack_response_t) + chunk_response_size;
249 : } else {
250 1 : *response_size = sizeof(spdm_chunk_send_ack_response_t);
251 : }
252 :
253 16 : return status;
254 : }
255 :
256 : #endif /* LIBSPDM_ENABLE_CAPABILITY_CHUNK_CAP */
|