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