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