microsoft/mu_feature_ffa

Public

mirrored from https://github.com/microsoft/mu_feature_ffaAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
89ca181ebb27a1bf7b8d4e233fb5945e10880437

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

FfaFeaturePkg/Library/TpmServiceLib/TpmServiceLib.c

626lines · modecode

1/** @file
2 Implementation for the TPM Service. This library is based
3 off of the ARM spec: TPM Service Command Response Buffer
4 Interface Over FF-A. The spec can be found here:
5
6 https://developer.arm.com/documentation/den0138/0100/?lang=en
7
8 The state flow is based off the TCG PC Client Specific Platform
9 TPM Profile for TPM 2.0. The spec can be found here:
10
11 https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/
12
13 Figure 4 - TPM State Diagram for CRB Interface
14
15 Copyright (c), Microsoft Corporation.
16 SPDX-License-Identifier: BSD-2-Clause-Patent
17
18**/
19
20#include <Uefi.h>
21#include <Library/DebugLib.h>
22#include <Library/BaseMemoryLib.h>
23#include <Library/TpmServiceLib.h>
24#include <Library/TpmServiceStateTranslationLib.h>
25#include <Guid/Tpm2ServiceFfa.h>
26#include <IndustryStandard/TpmPtp.h>
27#include <IndustryStandard/Tpm20.h>
28
29/* TPM Service Defines */
30#define TPM_MAJOR_VER (1)
31#define TPM_MINOR_VER (0)
32
33#define TPM_START_PROCESS_CMD (0)
34#define TPM_START_PROCESS_LOC_REQ (1)
35
36#define TPM_LOCALITY_OFFSET (0x1000)
37
38/* TPM Service States */
39typedef enum {
40 TPM_STATE_IDLE = 0,
41 TPM_STATE_READY,
42 TPM_STATE_COMPLETE,
43 NUM_TPM_STATES
44} TpmState;
45
46typedef UINTN TpmStatus;
47
48/* TPM Service Variables */
49STATIC TpmState mCurrentState;
50STATIC UINT8 mActiveLocality;
51STATIC PTP_CRB_INTERFACE_IDENTIFIER mInterfaceIdDefault;
52
53/**
54 Converts the passed in EFI_STATUS to a TPM_STATUS
55
56 @param Status The EFI_STATUS to convert
57
58 @retval TPM_STATUS_OK Success
59 @retval TPM_STATUS_INVARG Invalid parameter
60 @retval TPM_STATUS_DENIED Access denied
61 @retval TPM_STATUS_NOMEM Insufficient memory
62
63**/
64STATIC
65TpmStatus
66ConvertEfiToTpmStatus (
67 EFI_STATUS Status
68 )
69{
70 TpmStatus ReturnVal;
71
72 switch (Status) {
73 case EFI_SUCCESS:
74 ReturnVal = TPM2_FFA_SUCCESS_OK;
75 break;
76
77 case EFI_DEVICE_ERROR:
78 ReturnVal = TPM2_FFA_ERROR_DENIED;
79 break;
80
81 case EFI_BUFFER_TOO_SMALL:
82 ReturnVal = TPM2_FFA_ERROR_NOMEM;
83 break;
84
85 default:
86 ReturnVal = TPM2_FFA_ERROR_DENIED;
87 break;
88 }
89
90 return ReturnVal;
91}
92
93/**
94 Initializes the internal CRB
95
96 @param Locality The locality of the CRB
97
98**/
99STATIC
100VOID
101InitInternalCrb (
102 UINT8 Locality
103 )
104{
105 PTP_CRB_REGISTERS_PTR InternalTpmCrb;
106
107 InternalTpmCrb = (PTP_CRB_REGISTERS_PTR)(UINTN)(PcdGet64 (PcdTpmInternalBaseAddress) + (Locality * TPM_LOCALITY_OFFSET));
108 DEBUG ((DEBUG_INFO, "Locality: %x - InternalTpmCrb Address: %lx\n", Locality, (UINTN)InternalTpmCrb));
109 SetMem ((void *)InternalTpmCrb, sizeof (PTP_CRB_REGISTERS), 0x00);
110 InternalTpmCrb->LocalityState = PTP_CRB_LOCALITY_STATE_TPM_ESTABLISHED;
111 InternalTpmCrb->InterfaceId = mInterfaceIdDefault.Uint32;
112 InternalTpmCrb->CrbControlStatus = PTP_CRB_CONTROL_AREA_STATUS_TPM_IDLE;
113}
114
115/**
116 Cleans the internal CRB putting registers into a known good state
117
118**/
119STATIC
120VOID
121CleanInternalCrb (
122 VOID
123 )
124{
125 PTP_CRB_REGISTERS_PTR InternalTpmCrb;
126
127 /* If the user has never requested a locality, don't clean, no need.
128 * We should only ever clean the active locality as when localities change
129 * we clear the entire CRB region. */
130 if (mActiveLocality == NUM_LOCALITIES) {
131 return;
132 }
133
134 InternalTpmCrb = (PTP_CRB_REGISTERS_PTR)(UINTN)(PcdGet64 (PcdTpmInternalBaseAddress) + (mActiveLocality * TPM_LOCALITY_OFFSET));
135
136 /* Set the locality state based on the active locality. */
137 InternalTpmCrb->LocalityState = PTP_CRB_LOCALITY_STATE_TPM_ESTABLISHED;
138 switch (mActiveLocality) {
139 case 0:
140 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_ACTIVE_LOCALITY_0;
141 break;
142
143 case 1:
144 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_ACTIVE_LOCALITY_1;
145 break;
146
147 case 2:
148 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_ACTIVE_LOCALITY_2;
149 break;
150
151 case 3:
152 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_ACTIVE_LOCALITY_3;
153 break;
154
155 case 4:
156 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_ACTIVE_LOCALITY_4;
157 break;
158
159 default:
160 break;
161 }
162
163 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_TPM_REG_VALID_STATUS;
164 InternalTpmCrb->LocalityState |= PTP_CRB_LOCALITY_STATE_LOCALITY_ASSIGNED;
165 InternalTpmCrb->LocalityStatus |= PTP_CRB_LOCALITY_STATUS_GRANTED;
166 InternalTpmCrb->LocalityControl = 0;
167 InternalTpmCrb->InterfaceId = mInterfaceIdDefault.Uint32;
168 InternalTpmCrb->CrbControlExtension = 0;
169 InternalTpmCrb->CrbControlRequest = 0;
170 InternalTpmCrb->CrbControlCancel = 0;
171 InternalTpmCrb->CrbControlStart = 0;
172 InternalTpmCrb->CrbInterruptEnable = 0;
173 InternalTpmCrb->CrbInterruptStatus = 0;
174
175 /* Set the current TPM Status based on the current state. */
176 if (mCurrentState == TPM_STATE_IDLE) {
177 InternalTpmCrb->CrbControlStatus = PTP_CRB_CONTROL_AREA_STATUS_TPM_IDLE;
178 } else {
179 InternalTpmCrb->CrbControlStatus = 0;
180 }
181
182 /* Remaining registers can be ignored. */
183}
184
185/**
186 Handles commands for the TPM service
187
188 @retval TPM_STATUS_OK Success
189 @retval TPM_STATUS_INVARG Invalid parameter
190 @retval TPM_STATUS_DENIED Access denied
191 @retval TPM_STATUS_NOMEM Insufficient memory
192
193**/
194STATIC
195TpmStatus
196HandleCommand (
197 VOID
198 )
199{
200 EFI_STATUS Status;
201 PTP_CRB_REGISTERS_PTR InternalTpmCrb;
202
203 InternalTpmCrb = (PTP_CRB_REGISTERS_PTR)(UINTN)(PcdGet64 (PcdTpmInternalBaseAddress) + (mActiveLocality * TPM_LOCALITY_OFFSET));
204
205 /* Depending on our current state, we will investigate specific registers and
206 * make state transitions or deny commands. */
207 Status = EFI_ACCESS_DENIED;
208 switch (mCurrentState) {
209 /* The TPM can transition to IDLE from any state outside of command execution when the
210 * SW sets the goIdle bit in the CrbControlRequest register. When the TPM transitions to
211 * IDLE from COMPLETE it should clear the buffer. */
212 case TPM_STATE_IDLE:
213 /* Check the cmdReady bit in the CrbControlRequest register to see if we need to
214 * transition to the READY state, otherwise, deny the request. */
215 if (InternalTpmCrb->CrbControlRequest & PTP_CRB_CONTROL_AREA_REQUEST_COMMAND_READY) {
216 DEBUG ((DEBUG_INFO, "IDLE State - Handle TPM Command cmdReady Request\n"));
217 Status = TpmSstCmdReady (mActiveLocality);
218 if (Status == EFI_SUCCESS) {
219 mCurrentState = TPM_STATE_READY;
220 }
221 }
222
223 break;
224
225 /* The TPM can transition to READY from IDLE or COMPLETE when the SW sets the cmdReady bit
226 * in the CrbControlRequest register. When the TPM transitions to READY from COMPLETE it
227 * should clear the buffer. */
228 case TPM_STATE_READY:
229 /* Check the goIdle bit in the CrbControlRequest register to see if we need to
230 * transition back to the IDLE state. */
231 if (InternalTpmCrb->CrbControlRequest & PTP_CRB_CONTROL_AREA_REQUEST_GO_IDLE) {
232 DEBUG ((DEBUG_INFO, "READY State - Handle TPM Command goIdle Request\n"));
233 Status = TpmSstGoIdle (mActiveLocality);
234 if (Status == EFI_SUCCESS) {
235 mCurrentState = TPM_STATE_IDLE;
236 }
237
238 /* Check the cmdReady bit in the CrbControlRequest register, clear it if it has been
239 * set again. */
240 } else if (InternalTpmCrb->CrbControlRequest & PTP_CRB_CONTROL_AREA_REQUEST_COMMAND_READY) {
241 DEBUG ((DEBUG_INFO, "READY State - Handle TPM Command cmdReady Request\n"));
242 Status = TpmSstCmdReady (mActiveLocality);
243
244 /* Check the CrbControlStart register to see if we need to start executing a command.
245 * Once the command completes, transition to the COMPLETE state. */
246 } else if (InternalTpmCrb->CrbControlStart & PTP_CRB_CONTROL_START) {
247 DEBUG ((DEBUG_INFO, "READY State - Handle TPM Command Start Request\n"));
248 Status = TpmSstStart (mActiveLocality, InternalTpmCrb);
249 if (Status == EFI_SUCCESS) {
250 mCurrentState = TPM_STATE_COMPLETE;
251 }
252 }
253
254 break;
255
256 /* The TPM can transition to COMPLETE only from READY when the SW writes a 1 to the
257 * CrbControlStart register and the command execution finishes. The SW can write more
258 * data to the buffer and set the register again to trigger another command execution;
259 * this is only if TPM_CapCRBIdleBypass is 1. */
260 case TPM_STATE_COMPLETE:
261 /* Check the goIdle bit in the CrbControlRequest register to see if we need to
262 * transition to the IDLE state. */
263 if (InternalTpmCrb->CrbControlRequest & PTP_CRB_CONTROL_AREA_REQUEST_GO_IDLE) {
264 DEBUG ((DEBUG_INFO, "COMPLETE State - Handle TPM Command goIdle Request\n"));
265 Status = TpmSstGoIdle (mActiveLocality);
266 if (Status == EFI_SUCCESS) {
267 mCurrentState = TPM_STATE_IDLE;
268 SetMem ((void *)InternalTpmCrb->CrbDataBuffer, sizeof (InternalTpmCrb->CrbDataBuffer), 0x00);
269 }
270
271 /* Check the cmdReady bit in the CrbControlRequest register to see if we need to
272 * transition back to the READY state. */
273 } else if (InternalTpmCrb->CrbControlRequest & PTP_CRB_CONTROL_AREA_REQUEST_COMMAND_READY) {
274 /* Transition to READY from COMPLETE is only supported if TPM_CapCRBIdleBypass is 1.*/
275 if (TpmSstIsIdleBypassSupported ()) {
276 DEBUG ((DEBUG_INFO, "COMPLETE State - Handle TPM Command cmdReady Request\n"));
277 Status = TpmSstCmdReady (mActiveLocality);
278 if (Status == EFI_SUCCESS) {
279 mCurrentState = TPM_STATE_READY;
280 SetMem ((void *)InternalTpmCrb->CrbDataBuffer, sizeof (InternalTpmCrb->CrbDataBuffer), 0x00);
281 }
282 }
283
284 /* Check the CrbControlStart register to see if we need to execute another command. */
285 } else if (InternalTpmCrb->CrbControlStart & PTP_CRB_CONTROL_START) {
286 /* Execution of another command from COMPLETE is only supported if TPM_CapCRBIdleBypass
287 * is 1. */
288 if (TpmSstIsIdleBypassSupported ()) {
289 DEBUG ((DEBUG_INFO, "COMPLETE State - Handle TPM Command Start Request\n"));
290 Status = TpmSstStart (mActiveLocality, InternalTpmCrb);
291 }
292 }
293
294 break;
295
296 /* The normal state flow should be: IDLE -> READY -> COMPLETE -> IDLE. */
297 default:
298 DEBUG ((DEBUG_ERROR, "INVALID State - Attempting to transition to IDLE State\n"));
299 Status = TpmSstGoIdle (mActiveLocality);
300 if (Status == EFI_SUCCESS) {
301 mCurrentState = TPM_STATE_IDLE;
302 SetMem ((void *)InternalTpmCrb->CrbDataBuffer, sizeof (InternalTpmCrb->CrbDataBuffer), 0x00);
303 }
304
305 break;
306 }
307
308 /* Clear the internal CRB start register to indicate successful completion and response ready */
309 if (Status != EFI_SUCCESS) {
310 DEBUG ((DEBUG_ERROR, "Command Failed w/ Status: %x\n", Status));
311 }
312
313 return ConvertEfiToTpmStatus (Status);
314}
315
316/**
317 Handles locality requests for the TPM service
318
319 @param Locality The locality of the CRB
320
321 @retval TPM_STATUS_OK Success
322 @retval TPM_STATUS_INVARG Invalid parameter
323 @retval TPM_STATUS_DENIED Access denied
324 @retval TPM_STATUS_NOMEM Insufficient memory
325
326**/
327STATIC
328TpmStatus
329HandleLocalityRequest (
330 UINT8 Locality
331 )
332{
333 EFI_STATUS Status;
334
335 /* Request to use the TPM */
336 DEBUG ((DEBUG_INFO, "Handle TPM Locality%x Request\n", Locality));
337 Status = TpmSstLocalityRequest (Locality);
338
339 /* Update the internal TPM CRB */
340 if (Status == EFI_SUCCESS) {
341 InitInternalCrb (Locality);
342 mActiveLocality = Locality;
343 } else {
344 DEBUG ((DEBUG_ERROR, "Locality Request Failed w/ Status: %x\n", Status));
345 }
346
347 return ConvertEfiToTpmStatus (Status);
348}
349
350/**
351 Handler for TPM service GetInterfaceVersion command
352
353 @param Request The incoming message
354 @param Response The outgoing message
355
356 @retval TPM_STATUS_OK Success
357
358**/
359STATIC
360TpmStatus
361GetInterfaceVersionHandler (
362 DIRECT_MSG_ARGS_EX *Request,
363 DIRECT_MSG_ARGS_EX *Response
364 )
365{
366 TpmStatus ReturnVal;
367
368 ReturnVal = TPM2_FFA_SUCCESS_OK;
369 Response->Arg0 = TPM2_FFA_SUCCESS_OK_RESULTS_RETURNED;
370 Response->Arg1 = (TPM_MAJOR_VER << 16) | TPM_MINOR_VER;
371 return ReturnVal;
372}
373
374/**
375 Handler for TPM service GetFeatureInfo command
376
377 @param Request The incoming message
378 @param Response The outgoing message
379
380 @retval TPM_STATUS_OK Success
381
382**/
383STATIC
384TpmStatus
385GetFeatureInfoHandler (
386 DIRECT_MSG_ARGS_EX *Request,
387 DIRECT_MSG_ARGS_EX *Response
388 )
389{
390 TpmStatus ReturnVal;
391
392 ReturnVal = TPM2_FFA_SUCCESS_OK;
393 Response->Arg0 = TPM2_FFA_ERROR_NOTSUP;
394 DEBUG ((DEBUG_ERROR, "Unsupported Function\n"));
395 return ReturnVal;
396}
397
398/**
399 Handler for TPM service Start command
400
401 @param Request The incoming message
402 @param Response The outgoing message
403
404 @retval TPM_STATUS_OK Success
405 @retval TPM_STATUS_INVARG Invalid parameter
406 @retval TPM_STATUS_DENIED Access denied
407 @retval TPM_STATUS_NOMEM Insufficient memory
408
409**/
410STATIC
411TpmStatus
412StartHandler (
413 DIRECT_MSG_ARGS_EX *Request,
414 DIRECT_MSG_ARGS_EX *Response
415 )
416{
417 TpmStatus ReturnVal;
418 UINT8 Function;
419 UINT8 Locality;
420
421 ReturnVal = TPM2_FFA_SUCCESS_OK;
422 Function = Request->Arg1;
423 Locality = Request->Arg2;
424
425 /* Based on the function we will need to read from the corresponding address
426 * register in the tpm_crb to know where to pull the data from:
427 * NOTE: function = 0, command is ready to be processed
428 * function = 1, locality request is ready to be processed
429 * locality = 0...4, the locality where the command or request is located */
430 if (Locality >= NUM_LOCALITIES) {
431 Response->Arg0 = TPM2_FFA_ERROR_INVARG;
432 DEBUG ((DEBUG_ERROR, "Invalid Locality\n"));
433 return TPM2_FFA_ERROR_INVARG;
434 }
435
436 if (Function == TPM_START_PROCESS_CMD) {
437 /* We should only proceed if the locality being requested matches that of the
438 * current locality that is active. */
439 if (Locality == mActiveLocality) {
440 ReturnVal = HandleCommand ();
441 } else {
442 ReturnVal = TPM2_FFA_ERROR_INVARG;
443 DEBUG ((DEBUG_ERROR, "Locality Mismatch\n"));
444 }
445 } else if (Function == TPM_START_PROCESS_LOC_REQ) {
446 ReturnVal = HandleLocalityRequest (Locality);
447 } else {
448 ReturnVal = TPM2_FFA_ERROR_INVARG;
449 DEBUG ((DEBUG_ERROR, "Invalid Start Function\n"));
450 }
451
452 /* Clean up the internal CRB at the given locality */
453 CleanInternalCrb ();
454
455 Response->Arg0 = ReturnVal;
456 return ReturnVal;
457}
458
459/**
460 Handler for TPM service Register command
461
462 @param Request The incoming message
463 @param Response The outgoing message
464
465 @retval TPM_STATUS_OK Success
466
467**/
468STATIC
469TpmStatus
470RegisterHandler (
471 DIRECT_MSG_ARGS_EX *Request,
472 DIRECT_MSG_ARGS_EX *Response
473 )
474{
475 TpmStatus ReturnVal;
476
477 ReturnVal = TPM2_FFA_SUCCESS_OK;
478 Response->Arg0 = TPM2_FFA_ERROR_NOTSUP;
479 DEBUG ((DEBUG_ERROR, "Unsupported Function\n"));
480 return ReturnVal;
481}
482
483/**
484 Handler for TPM service Unregister command
485
486 @param Request The incoming message
487 @param Response The outgoing message
488
489 @retval TPM_STATUS_OK Success
490
491**/
492STATIC
493TpmStatus
494UnregisterHandler (
495 DIRECT_MSG_ARGS_EX *Request,
496 DIRECT_MSG_ARGS_EX *Response
497 )
498{
499 TpmStatus ReturnVal;
500
501 ReturnVal = TPM2_FFA_SUCCESS_OK;
502 Response->Arg0 = TPM2_FFA_ERROR_NOTSUP;
503 DEBUG ((DEBUG_ERROR, "Unsupported Function\n"));
504 return ReturnVal;
505}
506
507/**
508 Handler for TPM service Finish command
509
510 @param Request The incoming message
511 @param Response The outgoing message
512
513 @retval TPM_STATUS_OK Success
514
515**/
516STATIC
517TpmStatus
518FinishHandler (
519 DIRECT_MSG_ARGS_EX *Request,
520 DIRECT_MSG_ARGS_EX *Response
521 )
522{
523 TpmStatus ReturnVal;
524
525 ReturnVal = TPM2_FFA_SUCCESS_OK;
526 Response->Arg0 = TPM2_FFA_ERROR_NOTSUP;
527 DEBUG ((DEBUG_ERROR, "Unsupported Function\n"));
528 return ReturnVal;
529}
530
531/**
532 Initializes the TPM service
533
534**/
535VOID
536TpmServiceInit (
537 VOID
538 )
539{
540 UINT8 Locality;
541
542 /* Initialize the default interface ID. */
543 mInterfaceIdDefault.Uint32 = 0;
544 mInterfaceIdDefault.Bits.InterfaceType = 1; // CRB active
545 mInterfaceIdDefault.Bits.InterfaceVersion = 1; // CRB interface version
546 mInterfaceIdDefault.Bits.CapLocality = 1; // 5 localities supported
547 mInterfaceIdDefault.Bits.CapCRB = 1; // CRB supported
548
549 /* Initializes all of the localities. */
550 for (Locality = 0; Locality < NUM_LOCALITIES; Locality++) {
551 InitInternalCrb (Locality);
552 }
553
554 /* Initialize the TPM Service State Translation Library. */
555 TpmSstInit ();
556
557 /* Initialize our default state information. */
558 mCurrentState = TPM_STATE_IDLE;
559 mActiveLocality = NUM_LOCALITIES; // Invalid - No active locality
560}
561
562/**
563 De-initializes the TPM service
564
565**/
566VOID
567TpmServiceDeInit (
568 VOID
569 )
570{
571 /* Nothing to DeInit */
572}
573
574/**
575 Handler for TPM service commands
576
577 @param Request The incoming message
578 @param Response The outgoing message
579
580**/
581VOID
582TpmServiceHandle (
583 DIRECT_MSG_ARGS_EX *Request,
584 DIRECT_MSG_ARGS_EX *Response
585 )
586{
587 UINT64 Opcode;
588
589 /* Validate the input parameters before attempting to dereference or pass them along */
590 if ((Request == NULL) || (Response == NULL)) {
591 return;
592 }
593
594 Opcode = Request->Arg0;
595
596 switch (Opcode) {
597 case TPM2_FFA_GET_INTERFACE_VERSION:
598 GetInterfaceVersionHandler (Request, Response);
599 break;
600
601 case TPM2_FFA_GET_FEATURE_INFO:
602 GetFeatureInfoHandler (Request, Response);
603 break;
604
605 case TPM2_FFA_START:
606 StartHandler (Request, Response);
607 break;
608
609 case TPM2_FFA_REGISTER_FOR_NOTIFICATION:
610 RegisterHandler (Request, Response);
611 break;
612
613 case TPM2_FFA_UNREGISTER_FROM_NOTIFICATION:
614 UnregisterHandler (Request, Response);
615 break;
616
617 case TPM2_FFA_FINISH_NOTIFIED:
618 FinishHandler (Request, Response);
619 break;
620
621 default:
622 Response->Arg0 = TPM2_FFA_ERROR_NOFUNC;
623 DEBUG ((DEBUG_ERROR, "Invalid TPM Service Opcode\n"));
624 break;
625 }
626}
627