1 /*
2 * SPDX-FileCopyrightText: Copyright (c) 2022-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 * SPDX-License-Identifier: MIT
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24 /*!
25 * Provides the implementation for all GH100 specific SPDM HALs
26 * interfaces.
27 */
28
29 /* ------------------------ Includes --------------------------------------- */
30 #include "nvRmReg.h"
31 #include "gpu/spdm/spdm.h"
32 #include "spdm/rmspdmtransport.h"
33 #include "spdm/rmspdmvendordef.h"
34 #include "objtmr.h"
35 #include "gpu/gsp/kernel_gsp.h"
36 #include "gpu/bus/kern_bus.h"
37 #include "gpu/mem_mgr/mem_mgr.h"
38 #include "gpu/spdm/libspdm_includes.h"
39 #include "rmapi/client_resource.h"
40 #include "ctrl/ctrl2080/ctrl2080spdm.h"
41 #include "flcnretval.h"
42 #include "gpu/conf_compute/conf_compute.h"
43 #include "platform/sli/sli.h"
44 #include "nvspdm_rmconfig.h"
45 #include "published/hopper/gh100/dev_falcon_v4.h"
46 #include "gpu/conf_compute/conf_compute.h"
47
48 /* ------------------------ Macros ----------------------------------------- */
49 //
50 // List expected capabilties to receive from Responder.
51 // Regardless of whether Requester is configured to support these,
52 // we only expect Responder to provide these capabilities.
53 //
54
55 //
56 // TODO: SPDM_CAPABILITIES_FLAGS_GH100 and g_SpdmAlgoCheckTable_GH100 is expected capabilities flags
57 // and attributions what GH100 receive from responder. Currently, we have only 1 responder
58 // and return fixed capabilities flags and attributions.
59 // If we want to support different return capabilitis and attributions afterwards, we need
60 // to refactor spdmCheckConnection_GH100().
61 //
62 #define SPDM_CAPABILITIES_FLAGS_GH100 \
63 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_CERT_CAP | \
64 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MEAS_CAP_SIG | \
65 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MEAS_FRESH_CAP | \
66 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCRYPT_CAP | \
67 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MAC_CAP | \
68 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_EX_CAP | \
69 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_KEY_UPD_CAP | \
70 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_ENCAP_CAP | \
71 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MUT_AUTH_CAP | \
72 SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_HBEAT_CAP;
73
74 /* ------------------------ Static Variables ------------------------------- */
75 static SPDM_ALGO_CHECK_ENTRY g_SpdmAlgoCheckTable_GH100[] =
76 {
77 { LIBSPDM_DATA_MEASUREMENT_SPEC, SPDM_MEASUREMENT_SPECIFICATION_DMTF },
78 { LIBSPDM_DATA_MEASUREMENT_HASH_ALGO, SPDM_ALGORITHMS_MEASUREMENT_HASH_ALGO_TPM_ALG_SHA_384 },
79 { LIBSPDM_DATA_BASE_ASYM_ALGO, SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P384 },
80 { LIBSPDM_DATA_BASE_HASH_ALGO, SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_384 },
81 { LIBSPDM_DATA_DHE_NAME_GROUP, SPDM_ALGORITHMS_DHE_NAMED_GROUP_SECP_384_R1 },
82 { LIBSPDM_DATA_AEAD_CIPHER_SUITE, SPDM_ALGORITHMS_AEAD_CIPHER_SUITE_AES_256_GCM },
83 { LIBSPDM_DATA_KEY_SCHEDULE, SPDM_ALGORITHMS_KEY_SCHEDULE_HMAC_HASH },
84 { LIBSPDM_DATA_OTHER_PARAMS_SUPPORT, 0 },
85 #if LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP
86 { LIBSPDM_DATA_REQ_BASE_ASYM_ALG, SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSAPSS_3072 }
87 #endif
88 };
89
90 /* ------------------------ Static Function Prototypes --------------------- */
91 static void _spdmSendHeartbeat(NvU32 gpuInstance, void *pArgs);
92 static NV_STATUS _spdmTriggerHeartbeat(OBJGPU *pGpu, OBJTMR *pTmr, PTMR_EVENT pTmrEvent);
93
94 //
95 // Static transport layer functions which we pass to libspdm as function pointers.
96 // The libspdm library will then use these functions to send and receive SPDM messages.
97 // Function parameters and types must match those expected by libspdm.
98 //
99 static spdm_version_number_t _spdmGetSecuredMessageVersionGsp(spdm_version_number_t secured_message_version);
100
101 static uint8_t _spdmGetSecuredMessageSequenceNumberGsp(uint64_t sequence_number,
102 uint8_t *sequence_number_buffer);
103
104 static uint32_t _spdmGetSecuredMessageMaxRandomNumberCountGsp(void);
105
106 static libspdm_return_t _spdmEncodeMessageGsp(void *spdm_context, const uint32_t *session_id,
107 bool is_app_message, bool is_requester,
108 size_t message_size, void *message,
109 size_t *transport_message_size,
110 void **transport_message);
111
112 static libspdm_return_t _spdmDecodeMessageGsp(void *spdm_context, uint32_t **session_id,
113 bool *is_app_message, bool is_requester,
114 size_t transport_message_size, void *transport_message,
115 size_t *message_size, void **message);
116
117 static libspdm_return_t _spdmSendMessageGsp(void *spdm_context, size_t message_size,
118 const void *message, uint64_t timeout);
119
120 static libspdm_return_t _spdmReceiveMessageGsp(void *spdm_context, size_t *message_size,
121 void **message, uint64_t timeout);
122
123 /* ------------------------ Static Functions ------------------------------- */
124 //
125 // Hardcoding check for libspdm secured message callbacks version.
126 // If libspdm bumps this in a version update, we must update as well.
127 //
128 ct_assert(LIBSPDM_SECURED_MESSAGE_CALLBACKS_VERSION == 2);
129
130 /*!
131 * Callback to convert secured_message_version to DSP0277 version.
132 * In our case, secured_message_version will always be DSP0277 version.
133 */
134 spdm_version_number_t
_spdmGetSecuredMessageVersionGsp(spdm_version_number_t secured_message_version)135 _spdmGetSecuredMessageVersionGsp
136 (
137 spdm_version_number_t secured_message_version
138 )
139 {
140 return secured_message_version;
141 }
142
143 /*!
144 * @brief Work function scheduled by heartbeat callback in order to actually
145 * send the heartbeat to GSP-RM. Checks to ensure whether KEY_UPDATE
146 * is required before sending heartbeat.
147 *
148 * @param[in] gpuInstance : Instance number of the specific GPU
149 * @param[in] pArgs : Opaque pointer to the SPDM object
150 */
151 static void
_spdmSendHeartbeat(NvU32 gpuInstance,void * pArgs)152 _spdmSendHeartbeat
153 (
154 NvU32 gpuInstance,
155 void *pArgs
156 )
157 {
158 OBJGPU *pGpu = gpumgrGetGpu(gpuInstance);
159 OBJTMR *pTmr = NULL;
160 Spdm *pSpdm = NULL;
161 NV_STATUS status = NV_OK;
162
163 if (pGpu == NULL || pArgs == NULL)
164 {
165 status = NV_ERR_INVALID_ARGUMENT;
166 goto ErrorExit;
167 }
168
169 pTmr = GPU_GET_TIMER(pGpu);
170 pSpdm = (Spdm *)pArgs;
171 if (pSpdm == NULL || pSpdm->pLibspdmContext == NULL ||
172 pSpdm->sessionId == INVALID_SESSION_ID)
173 {
174 status = NV_ERR_NOT_READY;
175 goto ErrorExit;
176 }
177
178 //
179 // Check to see if KEY_UPDATE is required before using. As timer resets on
180 // every message sent, sending KEY_UPDATE shouldn't incur any timeout risk.
181 //
182 status = spdmCheckAndExecuteKeyUpdate(pGpu, pSpdm, NV_KEY_UPDATE_TRIGGER_ID_HEARTBEAT);
183 if (status != NV_OK)
184 {
185 goto ErrorExit;
186 }
187
188 CHECK_SPDM_STATUS(libspdm_heartbeat(pSpdm->pLibspdmContext, pSpdm->sessionId));
189
190 // Reschedule heartbeat only if successful
191 status = tmrEventScheduleRelSec(pTmr, pSpdm->pHeartbeatEvent, pSpdm->heartbeatPeriodSec);
192
193 ErrorExit:
194 if (status != NV_OK && pGpu != NULL)
195 {
196 ConfidentialCompute *pConfCompute = GPU_GET_CONF_COMPUTE(pGpu);
197 //
198 // Set GPU Ready State to false. This will destroy the SPDM session as well.
199 // Ideally we don't have dependency on Confidential Compute object here,
200 // but that's the best we can do without unnecessary code duplication.
201 //
202 confComputeSetErrorState(pGpu, pConfCompute);
203 }
204 }
205
206 /*!
207 * @brief Callback function scheduled when HEARTBEAT is enabled.
208 * Function will queue a work item to actually send the heartbeat,
209 * since we cannot do so in the callback interrupt context.
210 * Upon successful queueing of work item, schedules another heartbeat callback.
211 *
212 * @param[in] pGpu : OBJGPU pointer
213 * @param[in] pTmr : OBJTMR pointer
214 * @param[in] pTmrEvent : Pointer to the specific heartbeat timer event.
215 *
216 * @return NV_STATUS representing success or relevant failure.
217 */
218 static NV_STATUS
_spdmTriggerHeartbeat(OBJGPU * pGpu,OBJTMR * pTmr,PTMR_EVENT pTmrEvent)219 _spdmTriggerHeartbeat
220 (
221 OBJGPU *pGpu,
222 OBJTMR *pTmr,
223 PTMR_EVENT pTmrEvent
224 )
225 {
226 NV_STATUS status = NV_OK;
227
228 if (pGpu == NULL || pTmr == NULL || pTmrEvent == NULL)
229 {
230 status = NV_ERR_INVALID_ARGUMENT;
231 goto ErrorExit;
232 }
233
234 // Some RM APIs call SPDM, ensure we do not conflict with them
235 status = osQueueWorkItemWithFlags(pGpu, _spdmSendHeartbeat, pTmrEvent->pUserData,
236 OS_QUEUE_WORKITEM_FLAGS_DONT_FREE_PARAMS |
237 OS_QUEUE_WORKITEM_FLAGS_LOCK_API_RW |
238 OS_QUEUE_WORKITEM_FLAGS_LOCK_GPUS_RW);
239
240 ErrorExit:
241 if (status != NV_OK && pGpu != NULL)
242 {
243 ConfidentialCompute *pConfCompute = GPU_GET_CONF_COMPUTE(pGpu);
244 //
245 // Set GPU Ready State to false. This will destroy the SPDM session as well.
246 // Ideally we don't have dependency on Confidential Compute object here,
247 // but that's the best we can do without unnecessary code duplication.
248 //
249 confComputeSetErrorState(pGpu, pConfCompute);
250 }
251 return status;
252 }
253
254 /*!
255 * Callback to fill out sequence number in expected format.
256 * The sequence number is for secured message format only and defined in DMTF DSP0277.
257 * Currently, requester(RM) and responder(GSP-SPDM) doesn't support sequence number.
258 */
259 uint8_t
_spdmGetSecuredMessageSequenceNumberGsp(uint64_t sequence_number,uint8_t * sequence_number_buffer)260 _spdmGetSecuredMessageSequenceNumberGsp
261 (
262 uint64_t sequence_number,
263 uint8_t *sequence_number_buffer
264 )
265 {
266 // No sequence number included as a part of GSP secured message.
267 return 0;
268 }
269
270 /*!
271 * Callback to fill RNG blob in secured message.
272 * The random number size is for secured message format only and defined in DMTF DSP0277.
273 */
274 uint32_t
_spdmGetSecuredMessageMaxRandomNumberCountGsp(void)275 _spdmGetSecuredMessageMaxRandomNumberCountGsp
276 (
277 void
278 )
279 {
280 return NV_SPDM_MAX_RANDOM_MSG_BYTES;
281 }
282
283 /*!
284 * Static function libspdm uses as hook to RM<->GSP transport layer.
285 * If secured, encodes message into SPDM Secured Message format.
286 */
287 libspdm_return_t
_spdmEncodeMessageGsp(void * spdm_context,const uint32_t * session_id,bool is_app_message,bool is_requester,size_t message_size,void * message,size_t * transport_message_size,void ** transport_message)288 _spdmEncodeMessageGsp
289 (
290 void *spdm_context,
291 const uint32_t *session_id,
292 bool is_app_message,
293 bool is_requester,
294 size_t message_size,
295 void *message,
296 size_t *transport_message_size,
297 void **transport_message
298 )
299 {
300 libspdm_secured_message_callbacks_t securedMessageInfo;
301 libspdm_return_t status = LIBSPDM_STATUS_SUCCESS;
302 size_t securedMessageSize = 0;
303 void *pSecuredMessageContext = NULL;
304 NV_SPDM_DESC_HEADER *pNvSpdmDescHdr = NULL;
305 NvU32 payloadSize = 0;
306 Spdm *pSpdm = NULL;
307 size_t dataSize = sizeof(void *);
308
309 // Check libspdm parameters.
310 if (spdm_context == NULL || message == NULL || message_size == 0 ||
311 transport_message == NULL || *transport_message == NULL ||
312 transport_message_size == NULL)
313 {
314 return LIBSPDM_STATUS_INVALID_PARAMETER;
315 }
316
317 // Only support sending requester SPDM/Secured messages.
318 if (!is_requester || is_app_message)
319 {
320 return LIBSPDM_STATUS_INVALID_PARAMETER;
321 }
322
323 // The transport buffer must be large enough to fit the transport header and the message.
324 if (*transport_message_size < (sizeof(NV_SPDM_DESC_HEADER) + message_size))
325 {
326 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
327 }
328
329 status = libspdm_get_data(spdm_context, LIBSPDM_DATA_APP_CONTEXT_DATA,
330 NULL, (void *)&pSpdm, &dataSize);
331
332 if (status != LIBSPDM_STATUS_SUCCESS)
333 {
334 NV_PRINTF(LEVEL_ERROR, ", spdmStatus != LIBSPDM_STATUS_SUCCESS \n ");
335 return status;
336 }
337
338 if (pSpdm == NULL)
339 {
340 NV_PRINTF(LEVEL_ERROR, " pSpdm == NULL, SPDM context probably corrupted !! \n ");
341 return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
342 }
343
344 // Initialize descriptor header.
345 pNvSpdmDescHdr = (NV_SPDM_DESC_HEADER *)*transport_message;
346 portMemSet(pNvSpdmDescHdr, 0, sizeof(NV_SPDM_DESC_HEADER));
347 pNvSpdmDescHdr->version = NV_SPDM_DESC_HEADER_VERSION_CURRENT;
348
349 // Determine whether message is secured.
350 if (session_id != NULL)
351 {
352 pNvSpdmDescHdr->msgType = NV_SPDM_MESSAGE_TYPE_SECURED;
353
354 pSecuredMessageContext =
355 libspdm_get_secured_message_context_via_session_id(spdm_context, *session_id);
356
357 if (pSecuredMessageContext == NULL)
358 {
359 return LIBSPDM_STATUS_SESSION_MSG_ERROR;
360 }
361
362 // Calculate max space we have for secured message in transport buffer.
363 securedMessageSize = *transport_message_size;
364 securedMessageSize -= sizeof(NV_SPDM_DESC_HEADER);
365
366 // Initialize secured message attributes.
367 portMemSet(&securedMessageInfo, 0, sizeof(securedMessageInfo));
368 securedMessageInfo.version = LIBSPDM_SECURED_MESSAGE_CALLBACKS_VERSION;
369 securedMessageInfo.get_sequence_number = _spdmGetSecuredMessageSequenceNumberGsp;
370 securedMessageInfo.get_max_random_number_count = _spdmGetSecuredMessageMaxRandomNumberCountGsp;
371 securedMessageInfo.get_secured_spdm_version = _spdmGetSecuredMessageVersionGsp;
372
373 // Encode secured message into output buffer.
374 status = libspdm_encode_secured_message(pSecuredMessageContext, *session_id, is_requester,
375 message_size, message, &securedMessageSize,
376 (pNvSpdmDescHdr + 1), &securedMessageInfo);
377 if (status != LIBSPDM_STATUS_SUCCESS)
378 {
379 return status;
380 }
381
382 // Transport message buffer must be large enough to store secured message + NV_SPDM_DESC_HEADER.
383 NV_ASSERT_OR_RETURN(*transport_message_size > (securedMessageSize + sizeof(NV_SPDM_DESC_HEADER)),
384 LIBSPDM_STATUS_INVALID_MSG_FIELD);
385
386 pNvSpdmDescHdr->msgSizeByte = (NvU32)securedMessageSize;
387 *transport_message_size = securedMessageSize + sizeof(NV_SPDM_DESC_HEADER);
388 }
389 else
390 {
391 // The normal message is not encrypted, it will be sent as NV_SPDM_DESC_HEADER + Message.
392 payloadSize = sizeof(NV_SPDM_DESC_HEADER) + message_size;
393
394 //
395 // Check for large enough buffer for payload, as well as for overflow in
396 // operation above. If no overflow, we know message_size fits in NvU32.
397 //
398 if (*transport_message_size < payloadSize || payloadSize < message_size)
399 {
400 return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
401 }
402
403 //
404 // Fill in SPDM message header.
405 // The SPDM message is already after the SPDM msg header.
406 //
407 pNvSpdmDescHdr->msgType = NV_SPDM_MESSAGE_TYPE_NORMAL;
408 pNvSpdmDescHdr->msgSizeByte = (NvU32)message_size;
409 *transport_message_size = payloadSize;
410 }
411
412 // Check final encrypted message size.
413 if (*transport_message_size > pSpdm->payloadBufferSize)
414 {
415 return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
416 }
417
418 return LIBSPDM_STATUS_SUCCESS;
419 }
420
421 /*!
422 * Static function libspdm uses as hook to RM<->GSP transport layer.
423 * If secured, decodes the message from the SPDM Secured Message format.
424 */
425 libspdm_return_t
_spdmDecodeMessageGsp(void * spdm_context,uint32_t ** session_id,bool * is_app_message,bool is_requester,size_t transport_message_size,void * transport_message,size_t * message_size,void ** message)426 _spdmDecodeMessageGsp
427 (
428 void *spdm_context,
429 uint32_t **session_id,
430 bool *is_app_message,
431 bool is_requester,
432 size_t transport_message_size,
433 void *transport_message,
434 size_t *message_size,
435 void **message
436 )
437 {
438 libspdm_secured_message_callbacks_t securedMessageInfo;
439 NV_SPDM_DESC_HEADER *pNvSpdmDescHdr = NULL;
440 NvU32 payloadSize = 0;
441 void *pSecuredMessageContext = NULL;
442 libspdm_return_t status = LIBSPDM_STATUS_SUCCESS;
443 spdm_secured_message_a_data_header1_t *pSpdmSecuredMsgHdr = NULL;
444 Spdm *pSpdm = NULL;
445 size_t dataSize = sizeof(void *);
446
447 // Check libspdm parameters.
448 if (spdm_context == NULL || session_id == NULL || is_app_message == NULL ||
449 transport_message_size == 0 || transport_message == NULL ||
450 message_size == NULL || message == NULL || *message == NULL)
451 {
452 return LIBSPDM_STATUS_INVALID_PARAMETER;
453 }
454
455 // Only support receiving Responder SPDM/Secured messages.
456 if (is_requester)
457 {
458 return LIBSPDM_STATUS_INVALID_PARAMETER;
459 }
460
461 status = libspdm_get_data(spdm_context, LIBSPDM_DATA_APP_CONTEXT_DATA,
462 NULL, (void *)&pSpdm, &dataSize);
463
464 if (status != LIBSPDM_STATUS_SUCCESS)
465 {
466 NV_PRINTF(LEVEL_ERROR, " spdmStatus != LIBSPDM_STATUS_SUCCESS \n ");
467 return status;
468 }
469
470 if (pSpdm == NULL)
471 {
472 NV_PRINTF(LEVEL_ERROR, " pSpdm == NULL, SPDM context probably corrupted !! \n ");
473 return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
474 }
475
476 // Retrieve NV-header from message, and perform basic validation.
477 pNvSpdmDescHdr = (NV_SPDM_DESC_HEADER *)transport_message;
478 if (transport_message_size < sizeof(NV_SPDM_DESC_HEADER) ||
479 transport_message_size > pSpdm->payloadBufferSize)
480 {
481 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
482 }
483
484 if (pNvSpdmDescHdr->version != NV_SPDM_DESC_HEADER_VERSION_CURRENT)
485 {
486 NV_PRINTF(LEVEL_ERROR,
487 "SPDM: Version mismatch: [Check] version = 0x%x, [SpdmRet] version = 0x%x\n",
488 NV_SPDM_DESC_HEADER_VERSION_CURRENT, pNvSpdmDescHdr->version);
489 DBG_BREAKPOINT();
490 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
491 }
492
493 payloadSize = sizeof(NV_SPDM_DESC_HEADER) + pNvSpdmDescHdr->msgSizeByte;
494
495 if (transport_message_size != payloadSize)
496 {
497 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
498 }
499
500 // Decode message, based on type.
501 switch (pNvSpdmDescHdr->msgType)
502 {
503 case NV_SPDM_MESSAGE_TYPE_SECURED:
504 {
505 //
506 // Double-check the payload fits a secured message header.
507 // Our implementation of a secure message header only includes
508 // session ID - no sequence number.
509 //
510 if (pNvSpdmDescHdr->msgSizeByte < sizeof(spdm_secured_message_a_data_header1_t))
511 {
512 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
513 }
514
515 // Secured message header begins immediately after general NV header.
516 pSpdmSecuredMsgHdr = (spdm_secured_message_a_data_header1_t *)(pNvSpdmDescHdr + 1);
517 *session_id = &pSpdmSecuredMsgHdr->session_id;
518
519 pSecuredMessageContext =
520 libspdm_get_secured_message_context_via_session_id(spdm_context, **session_id);
521
522 if (pSecuredMessageContext == NULL)
523 {
524 return LIBSPDM_STATUS_SESSION_MSG_ERROR;
525 }
526
527 // Initialize secured message attributes.
528 portMemSet(&securedMessageInfo, 0, sizeof(securedMessageInfo));
529 securedMessageInfo.version = LIBSPDM_SECURED_MESSAGE_CALLBACKS_VERSION;
530 securedMessageInfo.get_sequence_number = _spdmGetSecuredMessageSequenceNumberGsp;
531 securedMessageInfo.get_max_random_number_count = _spdmGetSecuredMessageMaxRandomNumberCountGsp;
532 securedMessageInfo.get_secured_spdm_version = _spdmGetSecuredMessageVersionGsp;
533
534 // Decode and retrieve application payload from secured message.
535 // We must copy the message to the scratch buffer.
536 status = libspdm_decode_secured_message(pSecuredMessageContext,
537 **session_id, is_requester,
538 transport_message_size - sizeof(NV_SPDM_DESC_HEADER),
539 (pNvSpdmDescHdr + 1), message_size,
540 message, &securedMessageInfo);
541 if (status != LIBSPDM_STATUS_SUCCESS)
542 {
543 return status;
544 }
545 }
546 break;
547
548 case NV_SPDM_MESSAGE_TYPE_NORMAL:
549 {
550 // Indicate the message is unsecured.
551 *session_id = NULL;
552
553 //
554 // We both check that the buffer is large enough, and that the
555 // size_t type is large enough to hold the size of the message.
556 //
557 if (*message_size < pNvSpdmDescHdr->msgSizeByte)
558 {
559 return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
560 }
561
562 // The message is already present in the receiver buffer.
563 // Just use that.
564 *message = (uint8_t *)(pNvSpdmDescHdr + 1);
565 *message_size = pNvSpdmDescHdr->msgSizeByte;
566 }
567 break;
568
569 default:
570 {
571 return LIBSPDM_STATUS_INVALID_MSG_FIELD;
572 }
573 }
574
575 // We don't expect app message for any scenario.
576 *is_app_message = NV_FALSE;
577
578 return LIBSPDM_STATUS_SUCCESS;
579 }
580
581 /*!
582 * Static function libspdm uses as hook to RM<->GSP transport layer.
583 * Sends SPDM request message to GSP, and stores received response to buffer.
584 * Response buffer must be freed by corresponding _spdmReceiveMessageGsp().
585 */
586 libspdm_return_t
_spdmSendMessageGsp(void * spdm_context,size_t message_size,const void * message,uint64_t timeout)587 _spdmSendMessageGsp
588 (
589 void *spdm_context,
590 size_t message_size,
591 const void *message,
592 uint64_t timeout
593 )
594 {
595 NV_STATUS nvStatus = NV_OK;
596 libspdm_return_t spdmStatus = LIBSPDM_STATUS_SUCCESS;
597 Spdm *pSpdm = NULL;
598 OBJGPU *pGpu = NULL;
599 size_t dataSize = sizeof(void *);
600
601 // Check libspdm parameters.
602 if (message_size == 0 || message == NULL)
603 {
604 return LIBSPDM_STATUS_INVALID_PARAMETER;
605 }
606
607 spdmStatus = libspdm_get_data(spdm_context, LIBSPDM_DATA_APP_CONTEXT_DATA,
608 NULL, (void *)&pSpdm, &dataSize);
609
610 if (spdmStatus != LIBSPDM_STATUS_SUCCESS)
611 {
612 NV_PRINTF(LEVEL_ERROR," spdmStatus != LIBSPDM_STATUS_SUCCESS \n ");
613 return spdmStatus;
614 }
615
616 if (pSpdm == NULL)
617 {
618 NV_PRINTF(LEVEL_ERROR, " pSpdm == NULL, SPDM context probably corrupted !! \n ");
619 return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
620 }
621
622 pGpu = ENG_GET_GPU(pSpdm);
623
624 if (pGpu == NULL)
625 {
626 NV_PRINTF(LEVEL_ERROR, " pGpu == NULL, SPDM context probably corrupted !! \n ");
627 return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
628 }
629
630 // Ensure size is cleared to indicate no response pending in buffer yet
631 pSpdm->pendingResponseSize = 0;
632
633 if (pSpdm->transportBufferSize < message_size)
634 {
635 return LIBSPDM_STATUS_BUFFER_TOO_SMALL;
636 }
637
638 // Fill transport buffer with message and send
639 pSpdm->pendingResponseSize = pSpdm->transportBufferSize;
640 portMemCopy(pSpdm->pTransportBuffer, pSpdm->transportBufferSize, message, message_size);
641
642 nvStatus = spdmMessageProcess_HAL(pGpu, pSpdm,
643 pSpdm->pTransportBuffer, message_size,
644 pSpdm->pTransportBuffer, &pSpdm->pendingResponseSize);
645 if (nvStatus != NV_OK)
646 {
647 spdmStatus = LIBSPDM_STATUS_SEND_FAIL;
648 }
649
650 if (spdmStatus != LIBSPDM_STATUS_SUCCESS)
651 {
652 // If message failed, size is cleared to indicate no response pending
653 pSpdm->pendingResponseSize = 0;
654 }
655
656 return spdmStatus;
657 }
658
659 /*!
660 * Static function libspdm uses as hook to RM<->GSP transport layer.
661 * Copies stored response message back to libspdm buffer. Cannot be retried,
662 * as must free response message regardless of success or failure.
663 */
664 libspdm_return_t
_spdmReceiveMessageGsp(void * spdm_context,size_t * message_size,void ** message,uint64_t timeout)665 _spdmReceiveMessageGsp
666 (
667 void *spdm_context,
668 size_t *message_size,
669 void **message,
670 uint64_t timeout
671 )
672 {
673 libspdm_return_t spdmStatus = LIBSPDM_STATUS_SUCCESS;
674 Spdm *pSpdm = NULL;
675 size_t dataSize = sizeof(void *);
676
677 // Check libspdm parameters.
678 if (message_size == NULL || message == NULL || *message == NULL)
679 {
680 return LIBSPDM_STATUS_INVALID_PARAMETER;
681 }
682
683 spdmStatus = libspdm_get_data(spdm_context, LIBSPDM_DATA_APP_CONTEXT_DATA,
684 NULL, (void *)&pSpdm, &dataSize);
685
686 if (spdmStatus != LIBSPDM_STATUS_SUCCESS)
687 {
688 NV_PRINTF(LEVEL_ERROR, " spdmStatus != LIBSPDM_STATUS_SUCCESS \n ");
689 return spdmStatus;
690 }
691
692 if (pSpdm == NULL)
693 {
694 NV_PRINTF(LEVEL_ERROR, " pSpdm == NULL, SPDM context probably corrupted !! \n ");
695 return LIBSPDM_STATUS_INVALID_STATE_LOCAL;
696 }
697 // Basic validation to ensure we have a real response.
698 if (pSpdm->pendingResponseSize == 0 ||
699 pSpdm->pendingResponseSize > *message_size)
700 {
701 spdmStatus = LIBSPDM_STATUS_RECEIVE_FAIL;
702 goto ErrorExit;
703 }
704
705 portMemCopy(*message, *message_size,
706 pSpdm->pTransportBuffer, pSpdm->pendingResponseSize);
707 *message_size = pSpdm->pendingResponseSize;
708
709 ErrorExit:
710
711 // Ensure size is cleared to indicate no response pending in buffer
712 pSpdm->pendingResponseSize = 0;
713
714 return spdmStatus;
715 }
716
717
718 /* ------------------------ Public Functions ------------------------------- */
719 /*!
720 * On Hopper, we use GSP as SPDM Responder. To initialize, we must allocate
721 * a surface for SPDM communication, send initialization command to GSP-SPDM
722 * partition, and register all transport-layer functionality with library.
723 */
724 NV_STATUS
spdmDeviceInit_GH100(OBJGPU * pGpu,Spdm * pSpdm)725 spdmDeviceInit_GH100
726 (
727 OBJGPU *pGpu,
728 Spdm *pSpdm
729 )
730 {
731 if (pGpu == NULL || pSpdm == NULL || pSpdm->pLibspdmContext == NULL)
732 {
733 return NV_ERR_INVALID_ARGUMENT;
734 }
735
736 pSpdm->pendingResponseSize = 0;
737 pSpdm->pTransportBuffer = portMemAllocNonPaged(pSpdm->payloadBufferSize);
738 if (pSpdm->pTransportBuffer == NULL)
739 {
740 pSpdm->transportBufferSize = 0;
741 return NV_ERR_NO_MEMORY;
742 }
743 pSpdm->transportBufferSize = pSpdm->payloadBufferSize;
744
745 // Register transport layer functionality with library.
746 libspdm_register_transport_layer_func(pSpdm->pLibspdmContext,
747 NV_SPDM_MAX_SPDM_PAYLOAD_SIZE,
748 sizeof(NV_SPDM_DESC_HEADER),
749 0,
750 _spdmEncodeMessageGsp,
751 _spdmDecodeMessageGsp);
752
753 libspdm_register_device_io_func(pSpdm->pLibspdmContext,
754 _spdmSendMessageGsp,
755 _spdmReceiveMessageGsp);
756
757 pSpdm->bUsePolling = IS_GSP_CLIENT(pGpu);
758
759 return NV_OK;
760 }
761
762 /*!
763 * To deinitialize the GSP SPDM Responder, we need to release the surface for
764 * SPDM communication. GSP-RM will handle the rest.
765 */
766 NV_STATUS
spdmDeviceDeinit_GH100(OBJGPU * pGpu,Spdm * pSpdm,NvBool bForceClear)767 spdmDeviceDeinit_GH100
768 (
769 OBJGPU *pGpu,
770 Spdm *pSpdm,
771 NvBool bForceClear
772 )
773 {
774 // Just-in-case, portMemFree handles NULL.
775 portMemFree(pSpdm->pTransportBuffer);
776 pSpdm->pTransportBuffer = NULL;
777 pSpdm->transportBufferSize = 0;
778 pSpdm->pendingResponseSize = 0;
779
780 return NV_OK;
781 }
782
783 NV_STATUS
spdmMessageProcess_GH100(OBJGPU * pGpu,Spdm * pSpdm,NvU8 * pRequest,NvU32 requestSize,NvU8 * pResponse,NvU32 * pResponseSize)784 spdmMessageProcess_GH100
785 (
786 OBJGPU *pGpu,
787 Spdm *pSpdm,
788 NvU8 *pRequest,
789 NvU32 requestSize,
790 NvU8 *pResponse,
791 NvU32 *pResponseSize
792 )
793 {
794 NV_STATUS status = NV_OK;
795 NvU8 *pMapMem = NULL;
796 NvU32 messagePending = 0;
797 NvU8 *pPayloadBuffer = NULL;
798 NvU32 transportResponseSize = 0;
799 NV2080_CTRL_INTERNAL_SPDM_PARTITION_PARAMS params;
800 MemoryManager *pMemoryManager = NULL;
801 TRANSFER_SURFACE surf = {0};
802 NvBool bFreeShadowBuf = NV_FALSE;
803
804 if (pGpu == NULL || pSpdm == NULL ||pRequest == NULL ||
805 pResponse == NULL || pResponseSize == NULL)
806 {
807 return NV_ERR_INVALID_ARGUMENT;
808 }
809
810 if (requestSize > pSpdm->payloadBufferSize)
811 {
812 return NV_ERR_INSUFFICIENT_RESOURCES;
813 }
814
815 SLI_LOOP_START(SLI_LOOP_FLAGS_BC_ONLY)
816
817 // Offset is zero for all transfers
818 surf.offset = 0;
819 pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu);
820
821 if (pSpdm->bUsePolling)
822 {
823 //
824 // If we haven't established the session yet, we need to utilize polling
825 // based communication with GSP-SPDM partition, as GSP-RM RPC is not available.
826 // Note that there is a short window where session is established but GSP-RM RPC is
827 // not active - we don't expect any SPDM messages in this window.
828 //
829 RMTIMEOUT timeout;
830 KernelGsp *pKernelGsp = GPU_GET_KERNEL_GSP(pGpu);
831 KernelFalcon *pKernelFalcon = staticCast(pKernelGsp, KernelFalcon);
832
833 pPayloadBuffer = memmgrMemDescBeginTransfer(pMemoryManager, pSpdm->pPayloadBufferMemDesc,
834 TRANSFER_FLAGS_SHADOW_ALLOC);
835 if (pPayloadBuffer == NULL)
836 {
837 status = NV_ERR_INSUFFICIENT_RESOURCES;
838 goto ErrorExit;
839 }
840
841 // First copy payload to shared buffer
842 portMemCopy(pPayloadBuffer, requestSize, pRequest, requestSize);
843 memdescFlushCpuCaches(pGpu, pSpdm->pPayloadBufferMemDesc);
844
845 // Trigger message pending value, then poll for response from GSP
846 kflcnRegWrite_HAL(pGpu, pKernelFalcon, NV_PFALCON_FALCON_MAILBOX0, NV_SPDM_REQUESTER_MESSAGE_PENDING_TOKEN);
847
848 messagePending = kflcnRegRead_HAL(pGpu, pKernelFalcon, NV_PFALCON_FALCON_MAILBOX0);
849 gpuSetTimeout(pGpu, GPU_TIMEOUT_DEFAULT, &timeout, 0);
850 while (messagePending != NV_SPDM_RESPONDER_MESSAGE_PENDING_TOKEN)
851 {
852 if (gpuCheckTimeout(pGpu, &timeout) == NV_ERR_TIMEOUT)
853 {
854 NV_PRINTF(LEVEL_ERROR, "Timeout waiting for response from SPDM Responder!\n");
855 DBG_BREAKPOINT();
856 status = NV_ERR_TIMEOUT;
857 goto ErrorExit;
858 }
859
860 messagePending = kflcnRegRead_HAL(pGpu, pKernelFalcon, NV_PFALCON_FALCON_MAILBOX0);
861 }
862 }
863 else
864 {
865 //
866 // At this point, we have abandoned polling and now rely on GSP-RM RPCs.
867 //
868
869 // Copy entire SPDM message to shared memory.
870 surf.pMemDesc = pSpdm->pPayloadBufferMemDesc;
871 status = memmgrMemWrite(pMemoryManager, &surf, pRequest, requestSize, TRANSFER_FLAGS_NONE);
872 if (status != NV_OK)
873 {
874 goto ErrorExit;
875 }
876
877 // Prepare GSP-CMD and send to GSP-SPDM partition
878 portMemSet(¶ms, 0, sizeof(params));
879 params.cmd.cmdType = RM_GSP_SPDM_CMD_ID_CC_CTRL;
880
881 status = spdmCtrlSpdmPartition(pGpu, ¶ms);
882 if (params.msg.status != NV_OK)
883 {
884 NV_PRINTF(LEVEL_ERROR, "SPDM: RPC failed! RPC status = 0x%x\n",
885 params.msg.status);
886 status = params.msg.status;
887 DBG_BREAKPOINT();
888 goto ErrorExit;
889 }
890 }
891
892 if (status != NV_OK)
893 {
894 NV_PRINTF(LEVEL_ERROR, "SPDM: Send/receive failed! status = 0x%x\n", status);
895 DBG_BREAKPOINT();
896 goto ErrorExit;
897 }
898
899 // Retrieve descriptor header and response message, checking header to ensure valid response.
900 surf.pMemDesc = pSpdm->pPayloadBufferMemDesc;
901 pMapMem = portMemAllocNonPaged(pSpdm->payloadBufferSize);
902 if (pMapMem == NULL)
903 {
904 status = NV_ERR_INSUFFICIENT_RESOURCES;
905 goto ErrorExit;
906 }
907
908 bFreeShadowBuf = NV_TRUE;
909
910 status = memmgrMemRead(pMemoryManager, &surf, pMapMem,
911 pSpdm->payloadBufferSize,
912 TRANSFER_FLAGS_NONE);
913 if (status != NV_OK)
914 {
915 goto ErrorExit;
916 }
917
918 transportResponseSize = (((NV_SPDM_DESC_HEADER *)pMapMem)->msgSizeByte + sizeof(NV_SPDM_DESC_HEADER));
919 if (transportResponseSize > *pResponseSize)
920 {
921 status = NV_ERR_INSUFFICIENT_RESOURCES;
922 NV_PRINTF(LEVEL_ERROR, "SPDM: Error: transportResponseSize = 0x%x, responseSize = 0x%x\n",
923 transportResponseSize, *pResponseSize);
924 DBG_BREAKPOINT();
925 goto ErrorExit;
926 }
927
928 portMemCopy(pResponse, *pResponseSize, pMapMem, transportResponseSize);
929 *pResponseSize = transportResponseSize;
930
931 ErrorExit:
932 if (pPayloadBuffer != NULL)
933 {
934 memmgrMemDescEndTransfer(pMemoryManager, pSpdm->pPayloadBufferMemDesc,
935 TRANSFER_FLAGS_SHADOW_ALLOC);
936 }
937
938 SLI_LOOP_END
939
940 if (bFreeShadowBuf)
941 {
942 portMemFree(pMapMem);
943 bFreeShadowBuf = NV_FALSE;
944 }
945
946 return status;
947 }
948
949 NV_STATUS
spdmDeviceSecuredSessionSupported_GH100(OBJGPU * pGpu,Spdm * pSpdm)950 spdmDeviceSecuredSessionSupported_GH100
951 (
952 OBJGPU *pGpu,
953 Spdm *pSpdm
954 )
955 {
956 return NV_OK;
957 }
958
959 NV_STATUS
spdmCheckConnection_GH100(OBJGPU * pGpu,Spdm * pSpdm)960 spdmCheckConnection_GH100
961 (
962 OBJGPU *pGpu,
963 Spdm *pSpdm
964 )
965 {
966 void *pContext = NULL;
967 uint32_t expectedFlags = 0;
968 uint32_t capabilitiesFlags = 0;
969 libspdm_return_t ret = LIBSPDM_STATUS_SUCCESS;
970 libspdm_data_parameter_t dataParam;
971 libspdm_connection_state_t connectionState;
972 uint8_t ctExponent;
973 size_t dataSize;
974 NvU32 i;
975 NvU32 algoCheckCount;
976 NvU32 actualAlgo;
977 PSPDM_ALGO_CHECK_ENTRY pCheckEntry;
978
979 if (pGpu == NULL || pSpdm == NULL)
980 {
981 return NV_ERR_INVALID_ARGUMENT;
982 }
983
984 // Ensure we have at least negotiated the parameters of the SPDM connection.
985 pContext = (void *)pSpdm->pLibspdmContext;
986
987 if (pContext == NULL)
988 {
989 return NV_ERR_NOT_READY;
990 }
991
992 dataParam.location = LIBSPDM_DATA_LOCATION_CONNECTION;
993 dataSize = sizeof(connectionState);
994 ret = libspdm_get_data(pContext, LIBSPDM_DATA_CONNECTION_STATE,
995 &dataParam, &connectionState, &dataSize);
996
997 if (ret != LIBSPDM_STATUS_SUCCESS || connectionState < LIBSPDM_CONNECTION_STATE_NEGOTIATED)
998 {
999 return NV_ERR_NOT_READY;
1000 }
1001
1002 // Check version matches expected.
1003 if (libspdm_get_connection_version(pContext) != SPDM_MESSAGE_VERSION_11)
1004 {
1005 return NV_ERR_INVALID_STATE;
1006 }
1007
1008 dataSize = sizeof(ctExponent);
1009 ret = libspdm_get_data(pContext, LIBSPDM_DATA_CAPABILITY_CT_EXPONENT,
1010 &dataParam, &ctExponent, &dataSize);
1011
1012 if (ret != LIBSPDM_STATUS_SUCCESS || ctExponent != LIBSPDM_MAX_CT_EXPONENT)
1013 {
1014 NV_PRINTF(LEVEL_ERROR, "SPDM: Invalid Responder CT exponent.\n");
1015 return NV_ERR_INVALID_STATE;
1016 }
1017
1018 // Check all capabilities match expected.
1019 expectedFlags = SPDM_CAPABILITIES_FLAGS_GH100;
1020
1021 dataSize = sizeof(capabilitiesFlags);
1022 ret = libspdm_get_data(pContext, LIBSPDM_DATA_CAPABILITY_FLAGS,
1023 &dataParam, &capabilitiesFlags, &dataSize);
1024
1025 if (ret != LIBSPDM_STATUS_SUCCESS || capabilitiesFlags != expectedFlags)
1026 {
1027 NV_PRINTF(LEVEL_ERROR, "SPDM: Invalid Responder capabilities.\n");
1028 return NV_ERR_INVALID_STATE;
1029 }
1030
1031 // Check all crypto algorithms match expected.
1032 algoCheckCount = sizeof(g_SpdmAlgoCheckTable_GH100)/sizeof(SPDM_ALGO_CHECK_ENTRY);
1033
1034 for (i = 0; i < algoCheckCount; i++)
1035 {
1036 pCheckEntry = &g_SpdmAlgoCheckTable_GH100[i];
1037
1038 actualAlgo = 0;
1039 dataSize = sizeof(actualAlgo);
1040 ret = libspdm_get_data(pContext, pCheckEntry->dataType,
1041 &dataParam, &actualAlgo, &dataSize);
1042
1043 if (ret != LIBSPDM_STATUS_SUCCESS || actualAlgo != pCheckEntry->expectedAlgo)
1044 {
1045 NV_PRINTF(LEVEL_ERROR, "SPDM: Invalid crypto algorithms selected.\n");
1046 NV_PRINTF(LEVEL_ERROR, "SPDM: AlgoCheckCount 0x%0x, i is 0x%0x, status is 0x%0x.\n", (NvU32)algoCheckCount, (NvU32)i, (NvU32)ret);
1047 NV_PRINTF(LEVEL_ERROR, "SPDM: Expected algo 0x%0x, actual algo 0x%0x\n", (NvU32)pCheckEntry->expectedAlgo, (NvU32)actualAlgo);
1048 return NV_ERR_INVALID_STATE;
1049 }
1050 }
1051
1052 return NV_OK;
1053 }
1054
1055 NV_STATUS
spdmGetAttestationReport_GH100(OBJGPU * pGpu,Spdm * pSpdm,NvU8 * pNonce,void * pAttestationReport,NvU32 * pAttestationReportSize,NvBool * pbIsCecAttestationReportPresent,void * pCecAttestationReport,NvU32 * pCecAttestationReportSize)1056 spdmGetAttestationReport_GH100
1057 (
1058 OBJGPU *pGpu,
1059 Spdm *pSpdm,
1060 NvU8 *pNonce,
1061 void *pAttestationReport,
1062 NvU32 *pAttestationReportSize,
1063 NvBool *pbIsCecAttestationReportPresent,
1064 void *pCecAttestationReport,
1065 NvU32 *pCecAttestationReportSize
1066 )
1067 {
1068 NV_STATUS status = NV_OK;
1069 uint8_t numBlocks = 0;
1070 uint32_t measurementSize = 0;
1071 void *pMeasurementBuffer = NULL;
1072
1073 if (pGpu == NULL || pSpdm == NULL)
1074 {
1075 return NV_ERR_INVALID_ARGUMENT;
1076 }
1077
1078 // Ensure we have a valid session, and have retrieved the certificates.
1079 if (pSpdm->pLibspdmContext == NULL || !pSpdm->bSessionEstablished)
1080 {
1081 return NV_ERR_NOT_READY;
1082 }
1083
1084 // Check to see if KEY_UPDATE is required before using.
1085 status = spdmCheckAndExecuteKeyUpdate(pGpu, pSpdm, NV_KEY_UPDATE_TRIGGER_ID_GET_MEASUREMENTS);
1086 if (status != NV_OK)
1087 {
1088 return status;
1089 }
1090
1091 // Retrieve Attestation Report, if requested.
1092 if (pAttestationReport != NULL && pAttestationReportSize != NULL)
1093 {
1094 // Reset the libspdm message log to ensure it only contains the request and response from this call.
1095 libspdm_reset_msg_log(pSpdm->pLibspdmContext);
1096 libspdm_set_msg_log_mode(pSpdm->pLibspdmContext, LIBSPDM_MSG_LOG_MODE_ENABLE);
1097
1098 numBlocks = SPDM_MAX_MEASUREMENT_BLOCK_COUNT;
1099
1100 //
1101 // Use Attestation Report buffer temporarily to store measurements, we will replace
1102 // with the full Attestation Report from message transcripts. If the Attestation Report
1103 // buffer is too small to get the measurements, it won't be large enough for the report.
1104 //
1105 pMeasurementBuffer = pAttestationReport;
1106 measurementSize = *pAttestationReportSize;
1107
1108 // Request the Attestation Report using the provided nonce, signed by the AK cert.
1109 CHECK_SPDM_STATUS(libspdm_get_measurement_ex(pSpdm->pLibspdmContext, &pSpdm->sessionId,
1110 SPDM_GET_MEASUREMENTS_REQUEST_ATTRIBUTES_GENERATE_SIGNATURE,
1111 SPDM_GET_MEASUREMENTS_REQUEST_MEASUREMENT_OPERATION_ALL_MEASUREMENTS,
1112 SPDM_CERT_DEFAULT_SLOT_ID, NULL, &numBlocks,
1113 &measurementSize, pMeasurementBuffer, pNonce, NULL, NULL, NULL, NULL));
1114
1115 if (!nvspdm_check_and_clear_libspdm_assert())
1116 {
1117 // libspdm detects assert/error in GET_MEASUREMENT process, need to return error.
1118 NV_PRINTF(LEVEL_ERROR, "SPDM: spdmCheckAndClearLibspdmAssert() failed \n");
1119 DBG_BREAKPOINT();
1120
1121 return NV_ERR_INVALID_STATE;
1122 }
1123
1124 //
1125 // Message log buffer will hold Attestation Report, which is comprised of
1126 // the GET_MEASUREMENTS request concatenated with the MEASUREMENTS response.
1127 //
1128 if (*pAttestationReportSize < libspdm_get_msg_log_size(pSpdm->pLibspdmContext))
1129 {
1130 return NV_ERR_BUFFER_TOO_SMALL;
1131 }
1132
1133 portMemCopy(pAttestationReport, *pAttestationReportSize,
1134 pSpdm->pMsgLog, libspdm_get_msg_log_size(pSpdm->pLibspdmContext));
1135 *pAttestationReportSize = libspdm_get_msg_log_size(pSpdm->pLibspdmContext);
1136 }
1137
1138 // Retrieve CEC Attestation Report, if requested.
1139 if (pbIsCecAttestationReportPresent != NULL)
1140 {
1141 *pbIsCecAttestationReportPresent = NV_FALSE;
1142 }
1143
1144 ErrorExit:
1145 // Regardless of what happens, reset the message log so we don't track anything past this.
1146 libspdm_reset_msg_log(pSpdm->pLibspdmContext);
1147
1148 return status;
1149 }
1150
1151 /*!
1152 * Function that sends the opaque data from RM to GSP.
1153 */
1154 NV_STATUS
spdmSendInitRmDataCommand_GH100(OBJGPU * pGpu,Spdm * pSpdm)1155 spdmSendInitRmDataCommand_GH100
1156 (
1157 OBJGPU *pGpu,
1158 Spdm *pSpdm
1159 )
1160 {
1161 NV_STATUS status = NV_OK;
1162 RMTIMEOUT timeout;
1163 NV2080_CTRL_INTERNAL_SPDM_PARTITION_PARAMS params;
1164
1165 if (pGpu == NULL || pSpdm == NULL || !pSpdm->bSessionEstablished)
1166 {
1167 return NV_ERR_INVALID_ARGUMENT;
1168 }
1169
1170 portMemSet(¶ms, 0, sizeof(params));
1171 params.cmd.cmdType = RM_GSP_SPDM_CMD_ID_CC_INIT_RM_DATA;
1172
1173 gpuSetTimeout(pGpu, GPU_TIMEOUT_DEFAULT, &timeout, 0);
1174
1175 status = spdmCtrlSpdmPartition(pGpu, ¶ms);
1176 if (status != NV_OK)
1177 {
1178 NV_PRINTF(LEVEL_ERROR, "SPDM: Send/receive error in INIT_RM_DATA command\n");
1179 return NV_ERR_FLCN_ERROR;
1180 }
1181
1182 // Perform basic validation of header returned.
1183 status = params.msg.status;
1184 if (status != NV_OK)
1185 {
1186 NV_PRINTF(LEVEL_ERROR, "SPDM: RPC returned failure in INIT_RM_DATA command! status = 0x%0x\n",
1187 status);
1188 DBG_BREAKPOINT();
1189 return status;
1190 }
1191
1192 return NV_OK;
1193 }
1194
1195 /*!
1196 * @brief spdmCheckAndExecuteKeyUpdate_GH100
1197 * This function is used check scenario and perform key_exchange process if needed.
1198 *
1199 * @param[in] pGpu : OBJGPU Pointer
1200 * @param[in] pSpdm : SPDM pointer
1201 * @param[in] keyUpdateTriggerId : The id to identify the client, which trigger key update.
1202 *
1203 * @return NV_OK : Return NV_OK if no error
1204 *
1205 */
1206 NV_STATUS
spdmCheckAndExecuteKeyUpdate_GH100(OBJGPU * pGpu,Spdm * pSpdm,NvU32 keyUpdateTriggerId)1207 spdmCheckAndExecuteKeyUpdate_GH100
1208 (
1209 OBJGPU *pGpu,
1210 Spdm *pSpdm,
1211 NvU32 keyUpdateTriggerId
1212 )
1213 {
1214 libspdm_return_t ret;
1215 bool bSingleDirection = false;
1216
1217 if (pGpu == NULL || pSpdm == NULL)
1218 {
1219 return NV_ERR_INVALID_ARGUMENT;
1220 }
1221
1222 // Ensure we have a valid session, and have retrieved the certificates.
1223 if (pSpdm->pLibspdmContext == NULL || pSpdm->sessionId == INVALID_SESSION_ID)
1224 {
1225 return NV_ERR_NOT_READY;
1226 }
1227
1228 if (keyUpdateTriggerId == NV_KEY_UPDATE_TRIGGER_ID_GET_MEASUREMENTS ||
1229 keyUpdateTriggerId == NV_KEY_UPDATE_TRIGGER_ID_HEARTBEAT)
1230 {
1231 pSpdm->sessionMsgCount++;
1232 if (pSpdm->sessionMsgCount >= NV_KEY_UPDATE_TRIGGER_THRESHOLD)
1233 {
1234 ret = libspdm_key_update(pSpdm->pLibspdmContext, pSpdm->sessionId,
1235 bSingleDirection);
1236
1237 if (ret != LIBSPDM_STATUS_SUCCESS)
1238 {
1239 NV_PRINTF(LEVEL_ERROR, "Key Update (single direction(0x%x)) failed, ret(0x%x), triggerId = (0x%x).\n",
1240 bSingleDirection, ret, keyUpdateTriggerId);
1241 return NV_ERR_GENERIC;
1242 }
1243
1244 if (!nvspdm_check_and_clear_libspdm_assert())
1245 {
1246 // libspdm detects assert/error in key update process, need to return error.
1247 NV_PRINTF(LEVEL_ERROR, "SPDM: spdmCheckAndExecuteKeyUpdate() assert !! \n");
1248 DBG_BREAKPOINT();
1249 return NV_ERR_INVALID_STATE;
1250 }
1251
1252 NV_PRINTF(LEVEL_INFO, "SPDM: Key update successfully, triggerId = (0x%x)!\n", keyUpdateTriggerId);
1253 pSpdm->sessionMsgCount = 0;
1254 }
1255 }
1256 else
1257 {
1258 return NV_ERR_INVALID_ARGUMENT;
1259 }
1260
1261
1262 return NV_OK;
1263 }
1264
1265 NV_STATUS
spdmRegisterForHeartbeats_GH100(OBJGPU * pGpu,Spdm * pSpdm,NvU32 heartbeatPeriodSec)1266 spdmRegisterForHeartbeats_GH100
1267 (
1268 OBJGPU *pGpu,
1269 Spdm *pSpdm,
1270 NvU32 heartbeatPeriodSec
1271 )
1272 {
1273 NV_STATUS status = NV_OK;
1274 OBJTMR *pTmr;
1275 RMTIMEOUT timeout;
1276 NV2080_CTRL_INTERNAL_SPDM_PARTITION_PARAMS params;
1277
1278 // Basic parameter validation, make sure we are in a session
1279 if (pGpu == NULL || pSpdm == NULL || pSpdm->sessionId == INVALID_SESSION_ID)
1280 {
1281 return NV_ERR_NOT_READY;
1282 }
1283 // Set minimum value to ensure we don't trigger unexpected behavior with small timer values.
1284 else if (heartbeatPeriodSec < SPDM_DEFAULT_HEARTBEAT_PERIOD_IN_SEC)
1285 {
1286 return NV_ERR_INVALID_ARGUMENT;
1287 }
1288
1289 pTmr = GPU_GET_TIMER(pGpu);
1290
1291 // Create the timer event and schedule the first heartbeat callback
1292 status = tmrEventCreate(pTmr, &pSpdm->pHeartbeatEvent, _spdmTriggerHeartbeat, pSpdm, TMR_FLAGS_NONE);
1293 if (status != NV_OK)
1294 {
1295 return status;
1296 }
1297
1298 pSpdm->heartbeatPeriodSec = heartbeatPeriodSec;
1299 status = tmrEventScheduleRelSec(pTmr, pSpdm->pHeartbeatEvent, pSpdm->heartbeatPeriodSec);
1300
1301 // Tell GSP-RM to start expecting heartbeats.
1302 portMemSet(¶ms, 0, sizeof(params));
1303 params.cmd.cmdType = RM_GSP_SPDM_CMD_ID_CC_HEARTBEAT_CTRL;
1304 params.cmd.ccHeartbeatCtrl.bEnable = NV_TRUE;
1305
1306 gpuSetTimeout(pGpu, GPU_TIMEOUT_DEFAULT, &timeout, 0);
1307 status = spdmCtrlSpdmPartition(pGpu, ¶ms);
1308 if (status != NV_OK)
1309 {
1310 NV_PRINTF(LEVEL_ERROR, "SPDM: Send/receive error in CC_HEARTBEAT_CTRL command! Status = 0x%0x\n", status);
1311 return status;
1312 }
1313
1314 // Perform basic validation of header returned.
1315 status = params.msg.status;
1316 if (status != NV_OK)
1317 {
1318 NV_PRINTF(LEVEL_ERROR, "SPDM: RPC returned failure in CC_HEARTBEAT_CTRL command! status = 0x%0x\n",
1319 status);
1320 DBG_BREAKPOINT();
1321 return status;
1322 }
1323
1324 return status;
1325 }
1326
1327 NV_STATUS
spdmUnregisterFromHeartbeats_GH100(OBJGPU * pGpu,Spdm * pSpdm)1328 spdmUnregisterFromHeartbeats_GH100
1329 (
1330 OBJGPU *pGpu,
1331 Spdm *pSpdm
1332 )
1333 {
1334 NV_STATUS status = NV_OK;
1335
1336 OBJTMR *pTmr = GPU_GET_TIMER(pGpu);
1337 RMTIMEOUT timeout;
1338 NV2080_CTRL_INTERNAL_SPDM_PARTITION_PARAMS params;
1339
1340 if (pSpdm->pHeartbeatEvent == NULL)
1341 {
1342 // No timer exists, we never started sending heartbeats.
1343 return NV_OK;
1344 }
1345
1346 // Tell GSP-RM to stop expecting heartbeats.
1347 portMemSet(¶ms, 0, sizeof(params));
1348 params.cmd.cmdType = RM_GSP_SPDM_CMD_ID_CC_HEARTBEAT_CTRL;
1349 params.cmd.ccHeartbeatCtrl.bEnable = NV_FALSE;
1350
1351 gpuSetTimeout(pGpu, GPU_TIMEOUT_DEFAULT, &timeout, 0);
1352 status = spdmCtrlSpdmPartition(pGpu, ¶ms);
1353 if (status != NV_OK)
1354 {
1355 NV_PRINTF(LEVEL_ERROR, "SPDM: Send/receive error in CC_HEARTBEAT_CTRL command! Status = 0x%0x\n", status);
1356 goto ErrorExit;
1357 }
1358
1359 // Perform basic validation of header returned.
1360 status = params.msg.status;
1361 if (status != NV_OK)
1362 {
1363 NV_PRINTF(LEVEL_ERROR, "SPDM: RPC returned failure in CC_HEARTBEAT_CTRL command! status = 0x%0x\n",
1364 status);
1365 DBG_BREAKPOINT();
1366 goto ErrorExit;
1367 }
1368
1369 ErrorExit:
1370 // In any case, cancel any further heartbeats that might occur. Handles NULL gracefully.
1371 tmrEventCancel(pTmr, pSpdm->pHeartbeatEvent);
1372 tmrEventDestroy(pTmr, pSpdm->pHeartbeatEvent);
1373 pSpdm->pHeartbeatEvent = NULL;
1374 pSpdm->heartbeatPeriodSec = 0;
1375
1376 return status;
1377 }
1378