1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2022-2024 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  *                                                                          *
26  *      SPDM Object Module                                                  *
27  *                                                                          *
28  \**************************************************************************/
29 
30 /* ------------------------ Includes --------------------------------------- */
31 #include "gpu/spdm/spdm.h"
32 #include "spdm/rmspdmtransport.h"
33 #include "spdm/rmspdmvendordef.h"
34 #include "gpu/gpu.h"
35 #include "gpu/gpu_resource.h"
36 #include "nvspdm_rmconfig.h"
37 #include "gpu/mem_mgr/mem_mgr.h"
38 #include "platform/sli/sli.h"
39 #include "rmapi/client_resource.h"
40 #include "gpu/bus/kern_bus.h"
41 #include "os/os.h"
42 
43 //
44 // Libspdm only supported on certain builds,
45 // take note if you are copying header elsewhere.
46 //
47 #include "gpu/spdm/libspdm_includes.h"
48 
49 /* ------------------------ Static Function Prototypes --------------------- */
50 static void _spdmClearContext(Spdm *pSpdm);
51 libspdm_return_t _spdmAcquireTransportBuffer(void *context, void **msg_buf_ptr);
52 void _spdmReleaseTransportBuffer(void *context, const void *msg_buf_ptr);
53 bool _spdmVerifyCertChain(void *spdm_context, uint8_t slot_id, size_t cert_chain_size,
54                           const void *cert_chain, const void **trust_anchor, size_t *trust_anchor_size);
55 
56 /* ------------------------ Static Functions ------------------------------- */
57 /*
58  * Helper to clear and free any SPDM object context.
59  */
60 void
61 _spdmClearContext
62 (
63     Spdm *pSpdm
64 )
65 {
66     if (pSpdm == NULL)
67     {
68         return;
69     }
70 
71     //
72     // If we haven't deinitialized session, not much we can do now.
73     // Make best effort to free any allocated Requester context,
74     // ensuring we scrub the libspdm context.
75     //
76     if (pSpdm->pLibspdmContext != NULL)
77     {
78         libspdm_deinit_context(pSpdm->pLibspdmContext);
79         libspdm_reset_context(pSpdm->pLibspdmContext);
80         portMemSet((NvU8 *)pSpdm->pLibspdmContext, 0, pSpdm->libspdmContextSize);
81     }
82 
83     if (pSpdm->pLibspdmScratch != NULL)
84     {
85         portMemSet((NvU8 *)pSpdm->pLibspdmScratch, 0, pSpdm->libspdmScratchSize);
86     }
87 
88     // memdescFree and memdescDestroy handle NULL gracefully.
89     memdescFree(pSpdm->pPayloadBufferMemDesc);
90     memdescDestroy(pSpdm->pPayloadBufferMemDesc);
91 
92     pSpdm->pPayloadBufferMemDesc  = NULL;
93     pSpdm->payloadBufferSize      = 0;
94 
95     // portMemFree handles NULL pointers gracefully.
96     portMemFree(pSpdm->pLibspdmContext);
97     portMemFree(pSpdm->pLibspdmScratch);
98     portMemFree(pSpdm->pAttestationCertChain);
99     portMemFree(pSpdm->pDeviceIOContext);
100     portMemFree(pSpdm->pMsgLog);
101 
102     pSpdm->pLibspdmContext          = NULL;
103     pSpdm->pLibspdmScratch          = NULL;
104     pSpdm->pAttestationCertChain    = NULL;
105     pSpdm->pDeviceIOContext         = NULL;
106     pSpdm->pMsgLog                  = NULL;
107 
108     pSpdm->libspdmContextSize       = 0;
109     pSpdm->libspdmScratchSize       = 0;
110     pSpdm->attestationCertChainSize = 0;
111     pSpdm->msgLogMaxSize            = 0;
112 
113     pSpdm->sessionId                = INVALID_SESSION_ID;
114     pSpdm->bSessionEstablished      = NV_FALSE;
115     pSpdm->bUsePolling              = NV_FALSE;
116 }
117 
118 /*
119  * Transport layer helpers for send/receive message buffers.
120  */
121 libspdm_return_t
122 _spdmAcquireTransportBuffer
123 (
124     void     *context,
125     void   **msg_buf_ptr
126 )
127 {
128     if (context == NULL || msg_buf_ptr == NULL)
129     {
130         return LIBSPDM_STATUS_INVALID_PARAMETER;
131     }
132 
133     *msg_buf_ptr = portMemAllocNonPaged(NV_SPDM_SYSMEM_SURFACE_SIZE_IN_BYTES);
134     if (*msg_buf_ptr == NULL)
135     {
136         return LIBSPDM_STATUS_BUFFER_FULL;
137     }
138 
139     return LIBSPDM_STATUS_SUCCESS;
140 }
141 
142 void
143 _spdmReleaseTransportBuffer
144 (
145     void       *context,
146     const void *msg_buf_ptr
147 )
148 {
149     portMemFree((void *)msg_buf_ptr);
150 }
151 
152 bool
153 _spdmVerifyCertChain
154 (
155     void        *spdm_context,
156     uint8_t      slot_id,
157     size_t       cert_chain_size,
158     const void  *cert_chain,
159     const void **trust_anchor,
160     size_t       *trust_anchor_size
161 )
162 {
163     return NV_TRUE;
164 }
165 
166 /* ------------------------ Public Functions ------------------------------- */
167 /*!
168  * Constructor
169  */
170 NV_STATUS
171 spdmConstruct_IMPL
172 (
173     Spdm *pSpdm
174 )
175 {
176 
177     if (pSpdm == NULL)
178     {
179         return NV_ERR_INVALID_ARGUMENT;
180     }
181 
182     pSpdm->pLibspdmContext          = NULL;
183     pSpdm->pLibspdmScratch          = NULL;
184     pSpdm->pAttestationCertChain    = NULL;
185     pSpdm->pDeviceIOContext         = NULL;
186     pSpdm->pMsgLog                  = NULL;
187 
188     pSpdm->libspdmContextSize       = 0;
189     pSpdm->libspdmScratchSize       = 0;
190     pSpdm->attestationCertChainSize = 0;
191     pSpdm->msgLogMaxSize            = 0;
192 
193     pSpdm->sessionId                = INVALID_SESSION_ID;
194     pSpdm->bSessionEstablished      = NV_FALSE;
195     pSpdm->bUsePolling              = NV_FALSE;
196     pSpdm->bExportSecretCleared     = NV_FALSE;
197 
198     pSpdm->pPayloadBufferMemDesc    = NULL;
199     pSpdm->payloadBufferSize        = 0;
200 
201     pSpdm->pHeartbeatEvent          = NULL;
202     pSpdm->heartbeatPeriodSec       = 0;
203 
204     return NV_OK;
205 }
206 
207 /*!
208  * Destructor
209  */
210 void
211 spdmDestruct_IMPL
212 (
213     Spdm * pSpdm
214 )
215 {
216     _spdmClearContext(pSpdm);
217 }
218 
219 NV_STATUS
220 spdmSetupCommunicationBuffers_IMPL
221 (
222     OBJGPU *pGpu,
223     Spdm   *pSpdm
224 )
225 {
226     MemoryManager    *pMemoryManager = NULL;
227     TRANSFER_SURFACE  surf           = {0};
228     NV_STATUS         status         = NV_OK;
229 
230     // Create memory descriptor for payload buffer
231     status = memdescCreate(&pSpdm->pPayloadBufferMemDesc, pGpu, NV_SPDM_SYSMEM_SURFACE_SIZE_PAGE_ALIGNED,
232                            NV_SPDM_SYSMEM_SURFACE_ALIGNMENT_IN_BYTES, NV_TRUE, ADDR_SYSMEM,
233                            NV_MEMORY_CACHED, MEMDESC_FLAGS_ALLOC_IN_UNPROTECTED_MEMORY);
234     if (status != NV_OK || pSpdm->pPayloadBufferMemDesc == NULL)
235     {
236         status = NV_ERR_INSUFFICIENT_RESOURCES;
237         goto ErrorExit;
238     }
239 
240     memdescTagAlloc(status, NV_FB_ALLOC_RM_INTERNAL_OWNER_UNNAMED_TAG_82, pSpdm->pPayloadBufferMemDesc);
241     if (status != NV_OK)
242     {
243         goto ErrorExit;
244     }
245 
246     // We over-allocate since we must allocate page-aligned. Set size only to what we will use.
247     pSpdm->payloadBufferSize = NV_SPDM_SYSMEM_SURFACE_SIZE_IN_BYTES;
248 
249     // Scrub surface
250     SLI_LOOP_START(SLI_LOOP_FLAGS_BC_ONLY)
251     pMemoryManager = GPU_GET_MEMORY_MANAGER(pGpu);
252     surf.offset    = 0;
253 
254     surf.pMemDesc = pSpdm->pPayloadBufferMemDesc;
255     status = memmgrMemSet(pMemoryManager, &surf, 0, pSpdm->payloadBufferSize, TRANSFER_FLAGS_NONE);
256     if (status != NV_OK)
257     {
258         SLI_LOOP_GOTO(ErrorExit);
259     }
260 
261     SLI_LOOP_END
262 
263 ErrorExit:
264 
265     if (status != NV_OK)
266     {
267         _spdmClearContext(pSpdm);
268     }
269 
270     return status;
271 }
272 
273 NV_STATUS
274 spdmContextInit_IMPL
275 (
276     OBJGPU *pGpu,
277     Spdm   *pSpdm
278 )
279 {
280     NV_STATUS                status = NV_OK;
281     libspdm_data_parameter_t parameter;
282     uint8_t                  ctExponent;
283     uint32_t                 capFlags;
284     uint8_t                  measSpec;
285     uint32_t                 baseAsymAlgo;
286     uint32_t                 baseHashAlgo;
287     uint16_t                 dheGroup;
288     uint16_t                 aeadSuite;
289     uint16_t                 keySched;
290     uint32_t                 maxSessionCount;
291     uint8_t                  maxRetries;
292 
293 #if LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP
294     uint16_t                 reqAsymAlgo;
295     NvU8                    *pEncapCertChain;
296     NvU32                    encapCertChainSize;
297 #endif
298 
299     if (pGpu == NULL || pSpdm == NULL)
300     {
301         return NV_ERR_INVALID_ARGUMENT;
302     }
303 
304     // Allocate and initialize all required memory for context and certificates.
305     pSpdm->libspdmContextSize = libspdm_get_context_size();
306     pSpdm->pLibspdmContext    = portMemAllocNonPaged(pSpdm->libspdmContextSize);
307 
308     if (pSpdm->libspdmContextSize == 0 || pSpdm->pLibspdmContext == NULL)
309     {
310         status = NV_ERR_NO_MEMORY;
311         goto ErrorExit;
312     }
313 
314     portMemSet(pSpdm->pLibspdmContext, 0, pSpdm->libspdmContextSize);
315     libspdm_init_context(pSpdm->pLibspdmContext);
316 
317 
318     // Allocate message transcript recording buffer.
319     pSpdm->pMsgLog       = portMemAllocNonPaged(NV_SPDM_MAX_TRANSCRIPT_BUFFER_SIZE);
320     pSpdm->msgLogMaxSize = NV_SPDM_MAX_TRANSCRIPT_BUFFER_SIZE;
321 
322     if (pSpdm->pMsgLog == NULL)
323     {
324         pSpdm->msgLogMaxSize = 0;
325         status               = NV_ERR_NO_MEMORY;
326         goto ErrorExit;
327     }
328 
329 #if LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP
330     // Get requester cert chain for mutual authentication process.
331     pEncapCertChain = NULL;
332     encapCertChainSize = 0;
333     status = spdmGetReqEncapCertificates_HAL(pGpu, pSpdm, &pEncapCertChain, &encapCertChainSize);
334 
335     if (status != NV_OK || pEncapCertChain == NULL || encapCertChainSize == 0)
336     {
337         status = NV_ERR_NOT_SUPPORTED;
338         goto ErrorExit;
339     }
340 #endif
341 
342     //
343     // Eventually, owner of Spdm object may want to set their own
344     // specific configuration. For now, hardcode the only supported configuration.
345     //
346     portMemSet(&parameter, 0, sizeof(parameter));
347     parameter.location = LIBSPDM_DATA_LOCATION_LOCAL;
348 
349     // Requester will not check Responder's timing, set to maximum value.
350     ctExponent = LIBSPDM_MAX_CT_EXPONENT;
351     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_CAPABILITY_CT_EXPONENT,
352                                        &parameter, &ctExponent, sizeof(ctExponent)));
353 
354 #if LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP
355     capFlags = SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CERT_CAP     |
356                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_ENCRYPT_CAP  |
357                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_MAC_CAP      |
358                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_EX_CAP   |
359                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_UPD_CAP  |
360                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_ENCAP_CAP    |
361                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_MUT_AUTH_CAP |
362                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HBEAT_CAP;
363 #else
364     capFlags = SPDM_GET_CAPABILITIES_REQUEST_FLAGS_CERT_CAP    |
365                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_ENCRYPT_CAP |
366                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_MAC_CAP     |
367                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_EX_CAP  |
368                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_KEY_UPD_CAP |
369                SPDM_GET_CAPABILITIES_REQUEST_FLAGS_HBEAT_CAP;
370 #endif
371     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext,
372                                        LIBSPDM_DATA_CAPABILITY_FLAGS, &parameter,
373                                        &capFlags, sizeof(capFlags)));
374 
375     measSpec = SPDM_MEASUREMENT_SPECIFICATION_DMTF;
376     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext,
377                                        LIBSPDM_DATA_MEASUREMENT_SPEC, &parameter,
378                                        &measSpec, sizeof(measSpec)));
379 
380     baseAsymAlgo = SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_ECDSA_ECC_NIST_P384;
381     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_BASE_ASYM_ALGO,
382                                        &parameter, &baseAsymAlgo,
383                                        sizeof(baseAsymAlgo)));
384 
385     baseHashAlgo = SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_384;
386     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_BASE_HASH_ALGO,
387                                        &parameter, &baseHashAlgo,
388                                        sizeof(baseHashAlgo)));
389 
390     dheGroup = SPDM_ALGORITHMS_DHE_NAMED_GROUP_SECP_384_R1;
391     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_DHE_NAME_GROUP,
392                                        &parameter, &dheGroup, sizeof(dheGroup)));
393 
394     aeadSuite = SPDM_ALGORITHMS_AEAD_CIPHER_SUITE_AES_256_GCM;
395     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_AEAD_CIPHER_SUITE,
396                                        &parameter, &aeadSuite, sizeof(aeadSuite)));
397 
398     keySched = SPDM_ALGORITHMS_KEY_SCHEDULE_HMAC_HASH;
399     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_KEY_SCHEDULE,
400                                        &parameter, &keySched, sizeof(keySched)));
401 
402 #if LIBSPDM_ENABLE_CAPABILITY_MUT_AUTH_CAP
403     reqAsymAlgo = SPDM_ALGORITHMS_BASE_ASYM_ALGO_TPM_ALG_RSAPSS_3072;
404     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_REQ_BASE_ASYM_ALG,
405                                        &parameter, &reqAsymAlgo,
406                                        sizeof(reqAsymAlgo)));
407 
408     //
409     // Set certification for encapsulated command process.
410     // Specify certificate location, passing slot number as well.
411     //
412     parameter.additional_data[0] = SPDM_CERT_DEFAULT_SLOT_ID;
413     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_LOCAL_PUBLIC_CERT_CHAIN,
414                                        &parameter, pEncapCertChain,
415                                        encapCertChainSize));
416 #endif
417 
418     // Ensure that we set only DHE sessions as allowed, not PSK sessions.
419     maxSessionCount = 1;
420     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_MAX_DHE_SESSION_COUNT,
421                                        &parameter, &maxSessionCount, sizeof(maxSessionCount)));
422 
423     maxSessionCount = 0;
424     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_MAX_PSK_SESSION_COUNT,
425                                        &parameter, &maxSessionCount, sizeof(maxSessionCount)));
426 
427 
428     // We don't allow SPDM message retries.
429     maxRetries = 0;
430     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_REQUEST_RETRY_TIMES,
431                                        &parameter, &maxRetries, sizeof(maxRetries)));
432 
433     libspdm_init_msg_log(pSpdm->pLibspdmContext, pSpdm->pMsgLog, pSpdm->msgLogMaxSize);
434 
435 
436     // Store SPDM object pointer to libspdm context
437     CHECK_SPDM_STATUS(libspdm_set_data(pSpdm->pLibspdmContext, LIBSPDM_DATA_APP_CONTEXT_DATA,
438                                        NULL, (void *)&pSpdm, sizeof(void *)));
439 
440     //
441     // Perform any device-specific initialization. spdmDeviceInit is also
442     // responsible for registering transport layer functions with libspdm.
443     //
444     status = spdmDeviceInit_HAL(pGpu, pSpdm);
445     if (status != NV_OK)
446     {
447         goto ErrorExit;
448     }
449 
450     libspdm_register_device_buffer_func(pSpdm->pLibspdmContext,
451         NV_SPDM_SYSMEM_SURFACE_SIZE_IN_BYTES, NV_SPDM_SYSMEM_SURFACE_SIZE_IN_BYTES,
452         _spdmAcquireTransportBuffer, _spdmReleaseTransportBuffer,
453         _spdmAcquireTransportBuffer, _spdmReleaseTransportBuffer);
454 
455     //
456     // Allocate scratch space required for libspdm processing.
457     // We need to wait for transport layer initialization (i.e. after device init)
458     // in order to properly calculate the required scratch size.
459     //
460     pSpdm->libspdmScratchSize = libspdm_get_sizeof_required_scratch_buffer(pSpdm->pLibspdmContext);
461     pSpdm->pLibspdmScratch    = portMemAllocNonPaged(pSpdm->libspdmScratchSize);
462     if (pSpdm->libspdmScratchSize == 0 || pSpdm->pLibspdmScratch == NULL)
463     {
464         status = NV_ERR_NO_MEMORY;
465         goto ErrorExit;
466     }
467 
468     portMemSet(pSpdm->pLibspdmScratch, 0, pSpdm->libspdmScratchSize);
469     libspdm_set_scratch_buffer(pSpdm->pLibspdmContext, pSpdm->pLibspdmScratch,
470                                pSpdm->libspdmScratchSize);
471 
472     //
473     // Verifier is responsible for verifying the certificate chain. To avoid concerns
474     // with libspdm compatibility, override certificate validation function with stub.
475     //
476     libspdm_register_verify_spdm_cert_chain_func(pSpdm->pLibspdmContext, _spdmVerifyCertChain);
477 
478     // Initialize session message count to zero.
479     pSpdm->sessionMsgCount = 0;
480 
481 ErrorExit:
482 
483     // Clear all SPDM state on failure.
484     if (status != NV_OK)
485     {
486         _spdmClearContext(pSpdm);
487     }
488 
489     return status;
490 }
491 
492 NV_STATUS
493 spdmContextDeinit_IMPL
494 (
495     OBJGPU *pGpu,
496     Spdm   *pSpdm,
497     NvBool  bForceClear
498 )
499 {
500     NV_STATUS  status = NV_OK;
501 
502     if (pGpu == NULL || pSpdm == NULL)
503     {
504         return NV_ERR_INVALID_ARGUMENT;
505     }
506 
507     // If no session to end, just wipe state and return.
508     if (pSpdm->pLibspdmContext == NULL)
509     {
510         _spdmClearContext(pSpdm);
511         return NV_OK;
512     }
513 
514     //
515     // Make sure to unregister heartbeats in case we didn't
516     // hit normal teardown path. Best effort attempt.
517     //
518     status = spdmUnregisterFromHeartbeats(pGpu, pSpdm);
519     NV_ASSERT_OK(status);
520 
521     //
522     // End the session by deinitializing the Responder.
523     // We don't send END_SESSION as Responder will handle teardown.
524     //
525     NV_PRINTF(LEVEL_INFO, "SPDM: Tearing down session.\n");
526     status = spdmDeviceDeinit_HAL(pGpu, pSpdm, NV_TRUE);
527 
528     // Regardless of success or failure, clear any context.
529     _spdmClearContext(pSpdm);
530 
531     // We really shouldn't fail on deinitialization - ASSERT if we do.
532     NV_ASSERT_OK(status);
533     return status;
534 }
535 
536 NV_STATUS
537 spdmStart_IMPL
538 (
539     OBJGPU *pGpu,
540     Spdm   *pSpdm
541 )
542 {
543     NV_STATUS status = NV_OK;
544 
545     if (pGpu == NULL || pSpdm == NULL)
546     {
547         return NV_ERR_INVALID_ARGUMENT;
548     }
549 
550     if (pSpdm->pLibspdmContext == NULL)
551     {
552         return NV_ERR_NOT_READY;
553     }
554 
555      // Send GET_VERSION, GET_CAPABILITIES, and NEGOTIATE_ALGORITHMS to Responder.
556     NV_PRINTF(LEVEL_INFO, "SPDM: Starting new SPDM connection.\n");
557     CHECK_SPDM_STATUS(libspdm_init_connection(pSpdm->pLibspdmContext, NV_FALSE));
558 
559     if (!nvspdm_check_and_clear_libspdm_assert())
560     {
561         NV_PRINTF(LEVEL_ERROR, "SPDM: libspdm_init_connection() assert hit !!!.\n");
562         status =  NV_ERR_GENERIC;
563         goto ErrorExit;
564     }
565 
566     // Ensure the connection attributes match expected.
567     status = spdmCheckConnection_HAL(pGpu, pSpdm);
568     if (status != NV_OK)
569     {
570         NV_PRINTF(LEVEL_ERROR, "SPDM: Connection attributes did not match expected!\n");
571         goto ErrorExit;
572     }
573 
574     // Fetch the certificates from the responder and validate them
575     status = spdmGetCertificates_HAL(pGpu, pSpdm);
576     if (status != NV_OK)
577     {
578         NV_PRINTF(LEVEL_ERROR, "SPDM: Certificate retrieval failed!\n");
579         goto ErrorExit;
580     }
581 
582     if (!nvspdm_check_and_clear_libspdm_assert())
583     {
584         NV_PRINTF(LEVEL_ERROR, "SPDM: spdmGetCertificates_HAL() assert hit !!!.\n");
585         status =  NV_ERR_GENERIC;
586         goto ErrorExit;
587     }
588 
589     //
590     // Complete the SPDM handshake and start the secured session.
591     // Ensure we match type of sessionId parameter with what libspdm expects.
592     //
593     if (spdmDeviceSecuredSessionSupported_HAL(pGpu, pSpdm) == NV_OK)
594     {
595         NvU8 heartbeatPeriodInSec = 0;
596 
597         NV_PRINTF(LEVEL_INFO, "SPDM: Attempting to establish SPDM session.\n");
598         CHECK_SPDM_STATUS(libspdm_start_session(pSpdm->pLibspdmContext, NV_FALSE, NULL, 0,
599                                                 SPDM_KEY_EXCHANGE_REQUEST_NO_MEASUREMENT_SUMMARY_HASH,
600                                                 SPDM_CERT_DEFAULT_SLOT_ID, 0, &pSpdm->sessionId,
601                                                 &heartbeatPeriodInSec, NULL));
602         if (!nvspdm_check_and_clear_libspdm_assert())
603         {
604             NV_PRINTF(LEVEL_ERROR, "SPDM: libspdm_start_session() assert hit !!!.\n");
605             status = NV_ERR_GENERIC;
606             goto ErrorExit;
607         }
608         else if (heartbeatPeriodInSec != SPDM_DEFAULT_HEARTBEAT_PERIOD_IN_SEC)
609         {
610             //
611             // Do a basic check to make sure the SPDM heartbeat period agreed between
612             // Requester and Responder is expected, even if it is overridden via regkey.
613             //
614             NV_PRINTF(LEVEL_ERROR, "SPDM: Responder returned unexpected heartbeat 0x%x\n",
615                       heartbeatPeriodInSec);
616             status = NV_ERR_NOT_SUPPORTED;
617             goto ErrorExit;
618         }
619 
620         NV_PRINTF(LEVEL_INFO, "SPDM: Session establishment successful: sessionId 0x%x.\n",
621                   pSpdm->sessionId);
622         pSpdm->bSessionEstablished = NV_TRUE;
623         pSpdm->bUsePolling         = NV_FALSE;
624     }
625 
626 ErrorExit:
627 
628     //
629     // On error, set session as invalid. Don't need to reset context, since
630     // restarting SPDM exchange is valid scenario. Responder may not support.
631     //
632     if (status != NV_OK)
633     {
634         pSpdm->sessionId           = INVALID_SESSION_ID;
635         pSpdm->bSessionEstablished = NV_FALSE;
636         NV_PRINTF(LEVEL_ERROR, "SPDM: Session establishment failed!\n");
637         DBG_BREAKPOINT();
638     }
639 
640     return status;
641 }
642 
643 NV_STATUS
644 spdmRetrieveExportSecret_IMPL
645 (
646     OBJGPU *pGpu,
647     Spdm   *pSpdm,
648     NvU32   keySize,
649     NvU8   *pKeyOut
650 )
651 {
652     size_t                             keySizeSizeT    = 0;
653     libspdm_secured_message_context_t *pSessionContext = NULL;
654 
655     // Basic parameter validation.
656     if (pGpu == NULL || pSpdm == NULL || keySize == 0 || pKeyOut == NULL)
657     {
658         return NV_ERR_INVALID_ARGUMENT;
659     }
660 
661     // Ensure we are in valid state. Note that export master secret can only be retrieved once.
662     if (pSpdm->pLibspdmContext == NULL || !pSpdm->bSessionEstablished || pSpdm->bExportSecretCleared)
663     {
664         return NV_ERR_NOT_READY;
665     }
666 
667     pSessionContext =
668         libspdm_get_secured_message_context_via_session_id(pSpdm->pLibspdmContext, pSpdm->sessionId);
669     if (pSessionContext == NULL)
670     {
671         return NV_ERR_INVALID_STATE;
672     }
673 
674     //
675     // Fetch the export master secret.
676     // Use temporary size variable to handle type differences and avoid overflow.
677     //
678     keySizeSizeT = keySize;
679     if (!libspdm_secured_message_export_master_secret(pSessionContext, pKeyOut, &keySizeSizeT))
680     {
681         return NV_ERR_INVALID_STATE;
682     }
683 
684     // Clear the export master secret from SPDM memory.
685     libspdm_secured_message_clear_export_master_secret(pSessionContext);
686     pSpdm->bExportSecretCleared = NV_TRUE;
687 
688     return NV_OK;
689 }
690