2 The sample implementation for SMM variable protocol. And this driver
3 implements an SMI handler to communicate with the DXE runtime driver
4 to provide variable services.
6 Caution: This module requires additional review when modified.
7 This driver will have external input - variable data and communicate buffer in SMM mode.
8 This external input must be validated carefully to avoid security issue like
9 buffer overflow, integer overflow.
11 SmmVariableHandler() will receive untrusted input and do basic validation.
13 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
14 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
15 SmmVariableGetStatistics() should also do validation based on its own knowledge.
17 Copyright (c) 2010 - 2019, Intel Corporation. All rights reserved.<BR>
18 Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
19 SPDX-License-Identifier: BSD-2-Clause-Patent
23 #include <Protocol/SmmVariable.h>
24 #include <Protocol/SmmFirmwareVolumeBlock.h>
25 #include <Protocol/SmmFaultTolerantWrite.h>
26 #include <Protocol/MmEndOfDxe.h>
27 #include <Protocol/SmmVarCheck.h>
29 #include <Library/MmServicesTableLib.h>
30 #include <Library/VariablePolicyLib.h>
32 #include <Guid/SmmVariableCommon.h>
34 #include "VariableParsing.h"
35 #include "VariableRuntimeCache.h"
37 extern VARIABLE_STORE_HEADER
*mNvVariableCache
;
39 BOOLEAN mAtRuntime
= FALSE
;
40 UINT8
*mVariableBufferPayload
= NULL
;
41 UINTN mVariableBufferPayloadSize
;
44 SecureBoot Hook for SetVariable.
46 @param[in] VariableName Name of Variable to be found.
47 @param[in] VendorGuid Variable vendor GUID.
53 IN CHAR16
*VariableName
,
54 IN EFI_GUID
*VendorGuid
62 This code sets variable in storage blocks (Volatile or Non-Volatile).
64 @param VariableName Name of Variable to be found.
65 @param VendorGuid Variable vendor GUID.
66 @param Attributes Attribute value of the variable found
67 @param DataSize Size of Data found. If size is less than the
68 data, this value contains the required size.
69 @param Data Data pointer.
71 @return EFI_INVALID_PARAMETER Invalid parameter.
72 @return EFI_SUCCESS Set successfully.
73 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
74 @return EFI_NOT_FOUND Not found.
75 @return EFI_WRITE_PROTECTED Variable is read-only.
80 SmmVariableSetVariable (
81 IN CHAR16
*VariableName
,
82 IN EFI_GUID
*VendorGuid
,
91 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
93 mRequestSource
= VarCheckFromTrusted
;
94 Status
= VariableServiceSetVariable (
101 mRequestSource
= VarCheckFromUntrusted
;
105 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable
= {
106 VariableServiceGetVariable
,
107 VariableServiceGetNextVariableName
,
108 SmmVariableSetVariable
,
109 VariableServiceQueryVariableInfo
112 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck
= { VarCheckRegisterSetVariableCheckHandler
,
113 VarCheckVariablePropertySet
,
114 VarCheckVariablePropertyGet
};
117 Return TRUE if ExitBootServices () has been called.
119 @retval TRUE If ExitBootServices () has been called.
130 Initializes a basic mutual exclusion lock.
132 This function initializes a basic mutual exclusion lock to the released state
133 and returns the lock. Each lock provides mutual exclusion access at its task
134 priority level. Since there is no preemption or multiprocessor support in EFI,
135 acquiring the lock only consists of raising to the locks TPL.
136 If Lock is NULL, then ASSERT().
137 If Priority is not a valid TPL value, then ASSERT().
139 @param Lock A pointer to the lock data structure to initialize.
140 @param Priority EFI TPL is associated with the lock.
147 IN OUT EFI_LOCK
*Lock
,
155 Acquires lock only at boot time. Simply returns at runtime.
157 This is a temperary function that will be removed when
158 EfiAcquireLock() in UefiLib can handle the call in UEFI
159 Runtimer driver in RT phase.
160 It calls EfiAcquireLock() at boot time, and simply returns
163 @param Lock A pointer to the lock to acquire.
167 AcquireLockOnlyAtBootTime (
176 Releases lock only at boot time. Simply returns at runtime.
178 This is a temperary function which will be removed when
179 EfiReleaseLock() in UefiLib can handle the call in UEFI
180 Runtimer driver in RT phase.
181 It calls EfiReleaseLock() at boot time and simply returns
184 @param Lock A pointer to the lock to release.
188 ReleaseLockOnlyAtBootTime (
196 Retrieve the SMM Fault Tolerent Write protocol interface.
198 @param[out] FtwProtocol The interface of SMM Ftw protocol
200 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
201 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
202 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
207 OUT VOID
**FtwProtocol
213 // Locate Smm Fault Tolerent Write protocol
215 Status
= gMmst
->MmLocateProtocol (
216 &gEfiSmmFaultTolerantWriteProtocolGuid
,
225 Retrieve the SMM FVB protocol interface by HANDLE.
227 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
228 reading, writing, and erasing the target block.
229 @param[out] FvBlock The interface of SMM FVB protocol
231 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
232 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
233 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
238 IN EFI_HANDLE FvBlockHandle
,
239 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
243 // To get the SMM FVB protocol interface on the handle
245 return gMmst
->MmHandleProtocol (
247 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
254 Function returns an array of handles that support the SMM FVB protocol
255 in a buffer allocated from pool.
257 @param[out] NumberHandles The number of handles returned in Buffer.
258 @param[out] Buffer A pointer to the buffer to return the requested
259 array of handles that support SMM FVB protocol.
261 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
262 handles in Buffer was returned in NumberHandles.
263 @retval EFI_NOT_FOUND No SMM FVB handle was found.
264 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
265 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
269 GetFvbCountAndBuffer (
270 OUT UINTN
*NumberHandles
,
271 OUT EFI_HANDLE
**Buffer
277 if ((NumberHandles
== NULL
) || (Buffer
== NULL
)) {
278 return EFI_INVALID_PARAMETER
;
284 Status
= gMmst
->MmLocateHandle (
286 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
291 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
292 return EFI_NOT_FOUND
;
295 *Buffer
= AllocatePool (BufferSize
);
296 if (*Buffer
== NULL
) {
297 return EFI_OUT_OF_RESOURCES
;
300 Status
= gMmst
->MmLocateHandle (
302 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
308 *NumberHandles
= BufferSize
/ sizeof(EFI_HANDLE
);
309 if (EFI_ERROR(Status
)) {
320 Get the variable statistics information from the information buffer pointed by gVariableInfo.
322 Caution: This function may be invoked at SMM runtime.
323 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
325 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
326 On input, point to the variable information returned last time. if
327 InfoEntry->VendorGuid is zero, return the first information.
328 On output, point to the next variable information.
329 @param[in, out] InfoSize On input, the size of the variable information buffer.
330 On output, the returned variable information size.
332 @retval EFI_SUCCESS The variable information is found and returned successfully.
333 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
334 PcdVariableCollectStatistics should be set TRUE to support it.
335 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
336 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
340 SmmVariableGetStatistics (
341 IN OUT VARIABLE_INFO_ENTRY
*InfoEntry
,
342 IN OUT UINTN
*InfoSize
345 VARIABLE_INFO_ENTRY
*VariableInfo
;
347 UINTN StatisticsInfoSize
;
349 UINTN InfoNameMaxSize
;
352 if (InfoEntry
== NULL
) {
353 return EFI_INVALID_PARAMETER
;
356 VariableInfo
= gVariableInfo
;
357 if (VariableInfo
== NULL
) {
358 return EFI_UNSUPPORTED
;
361 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
);
362 if (*InfoSize
< StatisticsInfoSize
) {
363 *InfoSize
= StatisticsInfoSize
;
364 return EFI_BUFFER_TOO_SMALL
;
366 InfoName
= (CHAR16
*)(InfoEntry
+ 1);
367 InfoNameMaxSize
= (*InfoSize
- sizeof (VARIABLE_INFO_ENTRY
));
369 CopyGuid (&VendorGuid
, &InfoEntry
->VendorGuid
);
371 if (IsZeroGuid (&VendorGuid
)) {
373 // Return the first variable info
375 NameSize
= StrSize (VariableInfo
->Name
);
376 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
377 if (*InfoSize
< StatisticsInfoSize
) {
378 *InfoSize
= StatisticsInfoSize
;
379 return EFI_BUFFER_TOO_SMALL
;
381 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
382 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
383 *InfoSize
= StatisticsInfoSize
;
388 // Get the next variable info
390 while (VariableInfo
!= NULL
) {
391 if (CompareGuid (&VariableInfo
->VendorGuid
, &VendorGuid
)) {
392 NameSize
= StrSize (VariableInfo
->Name
);
393 if (NameSize
<= InfoNameMaxSize
) {
394 if (CompareMem (VariableInfo
->Name
, InfoName
, NameSize
) == 0) {
396 // Find the match one
398 VariableInfo
= VariableInfo
->Next
;
403 VariableInfo
= VariableInfo
->Next
;
406 if (VariableInfo
== NULL
) {
412 // Output the new variable info
414 NameSize
= StrSize (VariableInfo
->Name
);
415 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
416 if (*InfoSize
< StatisticsInfoSize
) {
417 *InfoSize
= StatisticsInfoSize
;
418 return EFI_BUFFER_TOO_SMALL
;
421 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
422 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
423 *InfoSize
= StatisticsInfoSize
;
430 Communication service SMI Handler entry.
432 This SMI handler provides services for the variable wrapper driver.
434 Caution: This function may receive untrusted input.
435 This variable data and communicate buffer are external input, so this function will do basic validation.
436 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
437 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
438 SmmVariableGetStatistics() should also do validation based on its own knowledge.
440 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
441 @param[in] RegisterContext Points to an optional handler context which was specified when the
442 handler was registered.
443 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
444 be conveyed from a non-SMM environment into an SMM environment.
445 @param[in, out] CommBufferSize The size of the CommBuffer.
447 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
448 should still be called.
449 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
451 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
453 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
458 IN EFI_HANDLE DispatchHandle
,
459 IN CONST VOID
*RegisterContext
,
460 IN OUT VOID
*CommBuffer
,
461 IN OUT UINTN
*CommBufferSize
465 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
466 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
467 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*GetNextVariableName
;
468 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*QueryVariableInfo
;
469 SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
*GetPayloadSize
;
470 SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
*RuntimeVariableCacheContext
;
471 SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
*GetRuntimeCacheInfo
;
472 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*VariableToLock
;
473 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*CommVariableProperty
;
474 VARIABLE_INFO_ENTRY
*VariableInfo
;
475 VARIABLE_RUNTIME_CACHE_CONTEXT
*VariableCacheContext
;
476 VARIABLE_STORE_HEADER
*VariableCache
;
478 UINTN NameBufferSize
;
479 UINTN CommBufferPayloadSize
;
480 UINTN TempCommBufferSize
;
483 // If input is invalid, stop processing this SMI
485 if (CommBuffer
== NULL
|| CommBufferSize
== NULL
) {
489 TempCommBufferSize
= *CommBufferSize
;
491 if (TempCommBufferSize
< SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
) {
492 DEBUG ((DEBUG_ERROR
, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
495 CommBufferPayloadSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
496 if (CommBufferPayloadSize
> mVariableBufferPayloadSize
) {
497 DEBUG ((DEBUG_ERROR
, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
501 if (!VariableSmmIsBufferOutsideSmmValid ((UINTN
)CommBuffer
, TempCommBufferSize
)) {
502 DEBUG ((DEBUG_ERROR
, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
506 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)CommBuffer
;
507 switch (SmmVariableFunctionHeader
->Function
) {
508 case SMM_VARIABLE_FUNCTION_GET_VARIABLE
:
509 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
510 DEBUG ((DEBUG_ERROR
, "GetVariable: SMM communication buffer size invalid!\n"));
514 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
516 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
517 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
518 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
519 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
521 // Prevent InfoSize overflow happen
523 Status
= EFI_ACCESS_DENIED
;
526 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
527 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
530 // SMRAM range check already covered before
532 if (InfoSize
> CommBufferPayloadSize
) {
533 DEBUG ((DEBUG_ERROR
, "GetVariable: Data size exceed communication buffer size limit!\n"));
534 Status
= EFI_ACCESS_DENIED
;
539 // The VariableSpeculationBarrier() call here is to ensure the previous
540 // range/content checks for the CommBuffer have been completed before the
541 // subsequent consumption of the CommBuffer content.
543 VariableSpeculationBarrier ();
544 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
546 // Make sure VariableName is A Null-terminated string.
548 Status
= EFI_ACCESS_DENIED
;
552 Status
= VariableServiceGetVariable (
553 SmmVariableHeader
->Name
,
554 &SmmVariableHeader
->Guid
,
555 &SmmVariableHeader
->Attributes
,
556 &SmmVariableHeader
->DataSize
,
557 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
559 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
562 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
:
563 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
564 DEBUG ((DEBUG_ERROR
, "GetNextVariableName: SMM communication buffer size invalid!\n"));
568 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
570 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
571 GetNextVariableName
= (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*) mVariableBufferPayload
;
572 if ((UINTN
)(~0) - GetNextVariableName
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
574 // Prevent InfoSize overflow happen
576 Status
= EFI_ACCESS_DENIED
;
579 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + GetNextVariableName
->NameSize
;
582 // SMRAM range check already covered before
584 if (InfoSize
> CommBufferPayloadSize
) {
585 DEBUG ((DEBUG_ERROR
, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
586 Status
= EFI_ACCESS_DENIED
;
590 NameBufferSize
= CommBufferPayloadSize
- OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
);
591 if (NameBufferSize
< sizeof (CHAR16
) || GetNextVariableName
->Name
[NameBufferSize
/sizeof (CHAR16
) - 1] != L
'\0') {
593 // Make sure input VariableName is A Null-terminated string.
595 Status
= EFI_ACCESS_DENIED
;
599 Status
= VariableServiceGetNextVariableName (
600 &GetNextVariableName
->NameSize
,
601 GetNextVariableName
->Name
,
602 &GetNextVariableName
->Guid
604 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
607 case SMM_VARIABLE_FUNCTION_SET_VARIABLE
:
608 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
609 DEBUG ((DEBUG_ERROR
, "SetVariable: SMM communication buffer size invalid!\n"));
613 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
615 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
616 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
617 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
618 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
620 // Prevent InfoSize overflow happen
622 Status
= EFI_ACCESS_DENIED
;
625 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
626 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
629 // SMRAM range check already covered before
630 // Data buffer should not contain SMM range
632 if (InfoSize
> CommBufferPayloadSize
) {
633 DEBUG ((DEBUG_ERROR
, "SetVariable: Data size exceed communication buffer size limit!\n"));
634 Status
= EFI_ACCESS_DENIED
;
639 // The VariableSpeculationBarrier() call here is to ensure the previous
640 // range/content checks for the CommBuffer have been completed before the
641 // subsequent consumption of the CommBuffer content.
643 VariableSpeculationBarrier ();
644 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
646 // Make sure VariableName is A Null-terminated string.
648 Status
= EFI_ACCESS_DENIED
;
652 Status
= VariableServiceSetVariable (
653 SmmVariableHeader
->Name
,
654 &SmmVariableHeader
->Guid
,
655 SmmVariableHeader
->Attributes
,
656 SmmVariableHeader
->DataSize
,
657 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
661 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
:
662 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
)) {
663 DEBUG ((DEBUG_ERROR
, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
666 QueryVariableInfo
= (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*) SmmVariableFunctionHeader
->Data
;
668 Status
= VariableServiceQueryVariableInfo (
669 QueryVariableInfo
->Attributes
,
670 &QueryVariableInfo
->MaximumVariableStorageSize
,
671 &QueryVariableInfo
->RemainingVariableStorageSize
,
672 &QueryVariableInfo
->MaximumVariableSize
676 case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE
:
677 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
)) {
678 DEBUG ((DEBUG_ERROR
, "GetPayloadSize: SMM communication buffer size invalid!\n"));
681 GetPayloadSize
= (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
*) SmmVariableFunctionHeader
->Data
;
682 GetPayloadSize
->VariablePayloadSize
= mVariableBufferPayloadSize
;
683 Status
= EFI_SUCCESS
;
686 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT
:
688 Status
= EFI_UNSUPPORTED
;
692 MorLockInitAtEndOfDxe ();
693 Status
= LockVariablePolicy ();
694 ASSERT_EFI_ERROR (Status
);
696 VarCheckLibInitializeAtEndOfDxe (NULL
);
698 // The initialization for variable quota.
700 InitializeVariableQuota ();
703 Status
= EFI_SUCCESS
;
706 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
:
708 Status
= EFI_SUCCESS
;
711 case SMM_VARIABLE_FUNCTION_GET_STATISTICS
:
712 VariableInfo
= (VARIABLE_INFO_ENTRY
*) SmmVariableFunctionHeader
->Data
;
713 InfoSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
716 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
717 // It is covered by previous CommBuffer check
721 // Do not need to check CommBufferSize buffer as it should point to SMRAM
722 // that was used by SMM core to cache CommSize from SmmCommunication protocol.
725 Status
= SmmVariableGetStatistics (VariableInfo
, &InfoSize
);
726 *CommBufferSize
= InfoSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
729 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE
:
731 Status
= EFI_ACCESS_DENIED
;
733 VariableToLock
= (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*) SmmVariableFunctionHeader
->Data
;
734 Status
= VariableLockRequestToLock (
736 VariableToLock
->Name
,
737 &VariableToLock
->Guid
741 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET
:
743 Status
= EFI_ACCESS_DENIED
;
745 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) SmmVariableFunctionHeader
->Data
;
746 Status
= VarCheckVariablePropertySet (
747 CommVariableProperty
->Name
,
748 &CommVariableProperty
->Guid
,
749 &CommVariableProperty
->VariableProperty
753 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET
:
754 if (CommBufferPayloadSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
755 DEBUG ((DEBUG_ERROR
, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
759 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
761 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
762 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) mVariableBufferPayload
;
763 if ((UINTN
) (~0) - CommVariableProperty
->NameSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
765 // Prevent InfoSize overflow happen
767 Status
= EFI_ACCESS_DENIED
;
770 InfoSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) + CommVariableProperty
->NameSize
;
773 // SMRAM range check already covered before
775 if (InfoSize
> CommBufferPayloadSize
) {
776 DEBUG ((DEBUG_ERROR
, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
777 Status
= EFI_ACCESS_DENIED
;
782 // The VariableSpeculationBarrier() call here is to ensure the previous
783 // range/content checks for the CommBuffer have been completed before the
784 // subsequent consumption of the CommBuffer content.
786 VariableSpeculationBarrier ();
787 if (CommVariableProperty
->NameSize
< sizeof (CHAR16
) || CommVariableProperty
->Name
[CommVariableProperty
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
789 // Make sure VariableName is A Null-terminated string.
791 Status
= EFI_ACCESS_DENIED
;
795 Status
= VarCheckVariablePropertyGet (
796 CommVariableProperty
->Name
,
797 &CommVariableProperty
->Guid
,
798 &CommVariableProperty
->VariableProperty
800 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
802 case SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT
:
803 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
)) {
804 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: SMM communication buffer size invalid!\n"));
805 Status
= EFI_ACCESS_DENIED
;
809 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Cannot init context after end of DXE!\n"));
810 Status
= EFI_ACCESS_DENIED
;
815 // Copy the input communicate buffer payload to the pre-allocated SMM variable payload buffer.
817 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
818 RuntimeVariableCacheContext
= (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
*) mVariableBufferPayload
;
821 // Verify required runtime cache buffers are provided.
823 if (RuntimeVariableCacheContext
->RuntimeVolatileCache
== NULL
||
824 RuntimeVariableCacheContext
->RuntimeNvCache
== NULL
||
825 RuntimeVariableCacheContext
->PendingUpdate
== NULL
||
826 RuntimeVariableCacheContext
->ReadLock
== NULL
||
827 RuntimeVariableCacheContext
->HobFlushComplete
== NULL
) {
828 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Required runtime cache buffer is NULL!\n"));
829 Status
= EFI_ACCESS_DENIED
;
834 // Verify minimum size requirements for the runtime variable store buffers.
836 if ((RuntimeVariableCacheContext
->RuntimeHobCache
!= NULL
&&
837 RuntimeVariableCacheContext
->RuntimeHobCache
->Size
< sizeof (VARIABLE_STORE_HEADER
)) ||
838 RuntimeVariableCacheContext
->RuntimeVolatileCache
->Size
< sizeof (VARIABLE_STORE_HEADER
) ||
839 RuntimeVariableCacheContext
->RuntimeNvCache
->Size
< sizeof (VARIABLE_STORE_HEADER
)) {
840 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: A runtime cache buffer size is invalid!\n"));
841 Status
= EFI_ACCESS_DENIED
;
846 // Verify runtime buffers do not overlap with SMRAM ranges.
848 if (RuntimeVariableCacheContext
->RuntimeHobCache
!= NULL
&&
849 !VariableSmmIsBufferOutsideSmmValid (
850 (UINTN
) RuntimeVariableCacheContext
->RuntimeHobCache
,
851 (UINTN
) RuntimeVariableCacheContext
->RuntimeHobCache
->Size
)) {
852 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime HOB cache buffer in SMRAM or overflow!\n"));
853 Status
= EFI_ACCESS_DENIED
;
856 if (!VariableSmmIsBufferOutsideSmmValid (
857 (UINTN
) RuntimeVariableCacheContext
->RuntimeVolatileCache
,
858 (UINTN
) RuntimeVariableCacheContext
->RuntimeVolatileCache
->Size
)) {
859 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime volatile cache buffer in SMRAM or overflow!\n"));
860 Status
= EFI_ACCESS_DENIED
;
863 if (!VariableSmmIsBufferOutsideSmmValid (
864 (UINTN
) RuntimeVariableCacheContext
->RuntimeNvCache
,
865 (UINTN
) RuntimeVariableCacheContext
->RuntimeNvCache
->Size
)) {
866 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime non-volatile cache buffer in SMRAM or overflow!\n"));
867 Status
= EFI_ACCESS_DENIED
;
870 if (!VariableSmmIsBufferOutsideSmmValid (
871 (UINTN
) RuntimeVariableCacheContext
->PendingUpdate
,
872 sizeof (*(RuntimeVariableCacheContext
->PendingUpdate
)))) {
873 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache pending update buffer in SMRAM or overflow!\n"));
874 Status
= EFI_ACCESS_DENIED
;
877 if (!VariableSmmIsBufferOutsideSmmValid (
878 (UINTN
) RuntimeVariableCacheContext
->ReadLock
,
879 sizeof (*(RuntimeVariableCacheContext
->ReadLock
)))) {
880 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache read lock buffer in SMRAM or overflow!\n"));
881 Status
= EFI_ACCESS_DENIED
;
884 if (!VariableSmmIsBufferOutsideSmmValid (
885 (UINTN
) RuntimeVariableCacheContext
->HobFlushComplete
,
886 sizeof (*(RuntimeVariableCacheContext
->HobFlushComplete
)))) {
887 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache HOB flush complete buffer in SMRAM or overflow!\n"));
888 Status
= EFI_ACCESS_DENIED
;
892 VariableCacheContext
= &mVariableModuleGlobal
->VariableGlobal
.VariableRuntimeCacheContext
;
893 VariableCacheContext
->VariableRuntimeHobCache
.Store
= RuntimeVariableCacheContext
->RuntimeHobCache
;
894 VariableCacheContext
->VariableRuntimeVolatileCache
.Store
= RuntimeVariableCacheContext
->RuntimeVolatileCache
;
895 VariableCacheContext
->VariableRuntimeNvCache
.Store
= RuntimeVariableCacheContext
->RuntimeNvCache
;
896 VariableCacheContext
->PendingUpdate
= RuntimeVariableCacheContext
->PendingUpdate
;
897 VariableCacheContext
->ReadLock
= RuntimeVariableCacheContext
->ReadLock
;
898 VariableCacheContext
->HobFlushComplete
= RuntimeVariableCacheContext
->HobFlushComplete
;
900 // Set up the intial pending request since the RT cache needs to be in sync with SMM cache
901 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateOffset
= 0;
902 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateLength
= 0;
903 if (mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
> 0 &&
904 VariableCacheContext
->VariableRuntimeHobCache
.Store
!= NULL
) {
905 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
;
906 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
907 CopyGuid (&(VariableCacheContext
->VariableRuntimeHobCache
.Store
->Signature
), &(VariableCache
->Signature
));
909 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.VolatileVariableBase
;
910 VariableCacheContext
->VariableRuntimeVolatileCache
.PendingUpdateOffset
= 0;
911 VariableCacheContext
->VariableRuntimeVolatileCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
912 CopyGuid (&(VariableCacheContext
->VariableRuntimeVolatileCache
.Store
->Signature
), &(VariableCache
->Signature
));
914 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mNvVariableCache
;
915 VariableCacheContext
->VariableRuntimeNvCache
.PendingUpdateOffset
= 0;
916 VariableCacheContext
->VariableRuntimeNvCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
917 CopyGuid (&(VariableCacheContext
->VariableRuntimeNvCache
.Store
->Signature
), &(VariableCache
->Signature
));
919 *(VariableCacheContext
->PendingUpdate
) = TRUE
;
920 *(VariableCacheContext
->ReadLock
) = FALSE
;
921 *(VariableCacheContext
->HobFlushComplete
) = FALSE
;
923 Status
= EFI_SUCCESS
;
925 case SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE
:
926 Status
= FlushPendingRuntimeVariableCacheUpdates ();
928 case SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO
:
929 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
)) {
930 DEBUG ((DEBUG_ERROR
, "GetRuntimeCacheInfo: SMM communication buffer size invalid!\n"));
933 GetRuntimeCacheInfo
= (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
*) SmmVariableFunctionHeader
->Data
;
935 if (mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
> 0) {
936 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
;
937 GetRuntimeCacheInfo
->TotalHobStorageSize
= VariableCache
->Size
;
939 GetRuntimeCacheInfo
->TotalHobStorageSize
= 0;
942 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.VolatileVariableBase
;
943 GetRuntimeCacheInfo
->TotalVolatileStorageSize
= VariableCache
->Size
;
944 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mNvVariableCache
;
945 GetRuntimeCacheInfo
->TotalNvStorageSize
= (UINTN
) VariableCache
->Size
;
946 GetRuntimeCacheInfo
->AuthenticatedVariableUsage
= mVariableModuleGlobal
->VariableGlobal
.AuthFormat
;
948 Status
= EFI_SUCCESS
;
952 Status
= EFI_UNSUPPORTED
;
957 SmmVariableFunctionHeader
->ReturnStatus
= Status
;
963 SMM END_OF_DXE protocol notification event handler.
965 @param Protocol Points to the protocol's unique identifier
966 @param Interface Points to the interface instance
967 @param Handle The handle on which the interface was installed
969 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
974 SmmEndOfDxeCallback (
975 IN CONST EFI_GUID
*Protocol
,
982 DEBUG ((DEBUG_INFO
, "[Variable]SMM_END_OF_DXE is signaled\n"));
983 MorLockInitAtEndOfDxe ();
984 Status
= LockVariablePolicy ();
985 ASSERT_EFI_ERROR (Status
);
987 VarCheckLibInitializeAtEndOfDxe (NULL
);
989 // The initialization for variable quota.
991 InitializeVariableQuota ();
992 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe
)) {
1000 Initializes variable write service for SMM.
1004 VariableWriteServiceInitializeSmm (
1010 Status
= VariableWriteServiceInitialize ();
1011 if (EFI_ERROR (Status
)) {
1012 DEBUG ((DEBUG_ERROR
, "Variable write service initialization failed. Status = %r\n", Status
));
1016 // Notify the variable wrapper driver the variable write service is ready
1018 VariableNotifySmmWriteReady ();
1022 SMM Fault Tolerant Write protocol notification event handler.
1024 Non-Volatile variable write may needs FTW protocol to reclaim when
1027 @param Protocol Points to the protocol's unique identifier
1028 @param Interface Points to the interface instance
1029 @param Handle The handle on which the interface was installed
1031 @retval EFI_SUCCESS SmmEventCallback runs successfully
1032 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
1037 SmmFtwNotificationEvent (
1038 IN CONST EFI_GUID
*Protocol
,
1040 IN EFI_HANDLE Handle
1044 EFI_PHYSICAL_ADDRESS VariableStoreBase
;
1045 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvbProtocol
;
1046 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL
*FtwProtocol
;
1047 EFI_PHYSICAL_ADDRESS NvStorageVariableBase
;
1048 UINTN FtwMaxBlockSize
;
1050 if (mVariableModuleGlobal
->FvbInstance
!= NULL
) {
1055 // Ensure SMM FTW protocol is installed.
1057 Status
= GetFtwProtocol ((VOID
**)&FtwProtocol
);
1058 if (EFI_ERROR (Status
)) {
1062 Status
= FtwProtocol
->GetMaxBlockSize (FtwProtocol
, &FtwMaxBlockSize
);
1063 if (!EFI_ERROR (Status
)) {
1064 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize
) <= FtwMaxBlockSize
);
1067 NvStorageVariableBase
= NV_STORAGE_VARIABLE_BASE
;
1068 VariableStoreBase
= NvStorageVariableBase
+ mNvFvHeaderCache
->HeaderLength
;
1071 // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready.
1073 mVariableModuleGlobal
->VariableGlobal
.NonVolatileVariableBase
= VariableStoreBase
;
1076 // Find the proper FVB protocol for variable.
1078 Status
= GetFvbInfoByAddress (NvStorageVariableBase
, NULL
, &FvbProtocol
);
1079 if (EFI_ERROR (Status
)) {
1080 return EFI_NOT_FOUND
;
1083 mVariableModuleGlobal
->FvbInstance
= FvbProtocol
;
1086 // Initializes variable write service after FTW was ready.
1088 VariableWriteServiceInitializeSmm ();
1095 Variable Driver main entry point. The Variable driver places the 4 EFI
1096 runtime services in the EFI System Table and installs arch protocols
1097 for variable read and write services being available. It also registers
1098 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
1100 @retval EFI_SUCCESS Variable service successfully initialized.
1105 MmVariableServiceInitialize (
1110 EFI_HANDLE VariableHandle
;
1111 VOID
*SmmFtwRegistration
;
1112 VOID
*SmmEndOfDxeRegistration
;
1115 // Variable initialize.
1117 Status
= VariableCommonInitialize ();
1118 ASSERT_EFI_ERROR (Status
);
1121 // Install the Smm Variable Protocol on a new handle.
1123 VariableHandle
= NULL
;
1124 Status
= gMmst
->MmInstallProtocolInterface (
1126 &gEfiSmmVariableProtocolGuid
,
1127 EFI_NATIVE_INTERFACE
,
1130 ASSERT_EFI_ERROR (Status
);
1132 Status
= gMmst
->MmInstallProtocolInterface (
1134 &gEdkiiSmmVarCheckProtocolGuid
,
1135 EFI_NATIVE_INTERFACE
,
1138 ASSERT_EFI_ERROR (Status
);
1140 mVariableBufferPayloadSize
= GetMaxVariableSize () +
1141 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) -
1142 GetVariableHeaderSize (mVariableModuleGlobal
->VariableGlobal
.AuthFormat
);
1144 Status
= gMmst
->MmAllocatePool (
1145 EfiRuntimeServicesData
,
1146 mVariableBufferPayloadSize
,
1147 (VOID
**)&mVariableBufferPayload
1149 ASSERT_EFI_ERROR (Status
);
1152 /// Register SMM variable SMI handler
1154 VariableHandle
= NULL
;
1155 Status
= gMmst
->MmiHandlerRegister (SmmVariableHandler
, &gEfiSmmVariableProtocolGuid
, &VariableHandle
);
1156 ASSERT_EFI_ERROR (Status
);
1159 // Notify the variable wrapper driver the variable service is ready
1161 VariableNotifySmmReady ();
1164 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
1166 Status
= gMmst
->MmRegisterProtocolNotify (
1167 &gEfiMmEndOfDxeProtocolGuid
,
1168 SmmEndOfDxeCallback
,
1169 &SmmEndOfDxeRegistration
1171 ASSERT_EFI_ERROR (Status
);
1173 if (!PcdGetBool (PcdEmuVariableNvModeEnable
)) {
1175 // Register FtwNotificationEvent () notify function.
1177 Status
= gMmst
->MmRegisterProtocolNotify (
1178 &gEfiSmmFaultTolerantWriteProtocolGuid
,
1179 SmmFtwNotificationEvent
,
1182 ASSERT_EFI_ERROR (Status
);
1184 SmmFtwNotificationEvent (NULL
, NULL
, NULL
);
1187 // Emulated non-volatile variable mode does not depend on FVB and FTW.
1189 VariableWriteServiceInitializeSmm ();