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>
31 #include <Guid/SmmVariableCommon.h>
33 #include "VariableParsing.h"
34 #include "VariableRuntimeCache.h"
36 extern VARIABLE_STORE_HEADER
*mNvVariableCache
;
38 BOOLEAN mAtRuntime
= FALSE
;
39 UINT8
*mVariableBufferPayload
= NULL
;
40 UINTN mVariableBufferPayloadSize
;
43 SecureBoot Hook for SetVariable.
45 @param[in] VariableName Name of Variable to be found.
46 @param[in] VendorGuid Variable vendor GUID.
52 IN CHAR16
*VariableName
,
53 IN EFI_GUID
*VendorGuid
61 This code sets variable in storage blocks (Volatile or Non-Volatile).
63 @param VariableName Name of Variable to be found.
64 @param VendorGuid Variable vendor GUID.
65 @param Attributes Attribute value of the variable found
66 @param DataSize Size of Data found. If size is less than the
67 data, this value contains the required size.
68 @param Data Data pointer.
70 @return EFI_INVALID_PARAMETER Invalid parameter.
71 @return EFI_SUCCESS Set successfully.
72 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
73 @return EFI_NOT_FOUND Not found.
74 @return EFI_WRITE_PROTECTED Variable is read-only.
79 SmmVariableSetVariable (
80 IN CHAR16
*VariableName
,
81 IN EFI_GUID
*VendorGuid
,
90 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
92 mRequestSource
= VarCheckFromTrusted
;
93 Status
= VariableServiceSetVariable (
100 mRequestSource
= VarCheckFromUntrusted
;
104 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable
= {
105 VariableServiceGetVariable
,
106 VariableServiceGetNextVariableName
,
107 SmmVariableSetVariable
,
108 VariableServiceQueryVariableInfo
111 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck
= { VarCheckRegisterSetVariableCheckHandler
,
112 VarCheckVariablePropertySet
,
113 VarCheckVariablePropertyGet
};
116 Return TRUE if ExitBootServices () has been called.
118 @retval TRUE If ExitBootServices () has been called.
129 Initializes a basic mutual exclusion lock.
131 This function initializes a basic mutual exclusion lock to the released state
132 and returns the lock. Each lock provides mutual exclusion access at its task
133 priority level. Since there is no preemption or multiprocessor support in EFI,
134 acquiring the lock only consists of raising to the locks TPL.
135 If Lock is NULL, then ASSERT().
136 If Priority is not a valid TPL value, then ASSERT().
138 @param Lock A pointer to the lock data structure to initialize.
139 @param Priority EFI TPL is associated with the lock.
146 IN OUT EFI_LOCK
*Lock
,
154 Acquires lock only at boot time. Simply returns at runtime.
156 This is a temperary function that will be removed when
157 EfiAcquireLock() in UefiLib can handle the call in UEFI
158 Runtimer driver in RT phase.
159 It calls EfiAcquireLock() at boot time, and simply returns
162 @param Lock A pointer to the lock to acquire.
166 AcquireLockOnlyAtBootTime (
175 Releases lock only at boot time. Simply returns at runtime.
177 This is a temperary function which will be removed when
178 EfiReleaseLock() in UefiLib can handle the call in UEFI
179 Runtimer driver in RT phase.
180 It calls EfiReleaseLock() at boot time and simply returns
183 @param Lock A pointer to the lock to release.
187 ReleaseLockOnlyAtBootTime (
195 Retrieve the SMM Fault Tolerent Write protocol interface.
197 @param[out] FtwProtocol The interface of SMM Ftw protocol
199 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
200 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
201 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
206 OUT VOID
**FtwProtocol
212 // Locate Smm Fault Tolerent Write protocol
214 Status
= gMmst
->MmLocateProtocol (
215 &gEfiSmmFaultTolerantWriteProtocolGuid
,
224 Retrieve the SMM FVB protocol interface by HANDLE.
226 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
227 reading, writing, and erasing the target block.
228 @param[out] FvBlock The interface of SMM FVB protocol
230 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
231 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
232 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
237 IN EFI_HANDLE FvBlockHandle
,
238 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
242 // To get the SMM FVB protocol interface on the handle
244 return gMmst
->MmHandleProtocol (
246 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
253 Function returns an array of handles that support the SMM FVB protocol
254 in a buffer allocated from pool.
256 @param[out] NumberHandles The number of handles returned in Buffer.
257 @param[out] Buffer A pointer to the buffer to return the requested
258 array of handles that support SMM FVB protocol.
260 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
261 handles in Buffer was returned in NumberHandles.
262 @retval EFI_NOT_FOUND No SMM FVB handle was found.
263 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
264 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
268 GetFvbCountAndBuffer (
269 OUT UINTN
*NumberHandles
,
270 OUT EFI_HANDLE
**Buffer
276 if ((NumberHandles
== NULL
) || (Buffer
== NULL
)) {
277 return EFI_INVALID_PARAMETER
;
283 Status
= gMmst
->MmLocateHandle (
285 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
290 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
291 return EFI_NOT_FOUND
;
294 *Buffer
= AllocatePool (BufferSize
);
295 if (*Buffer
== NULL
) {
296 return EFI_OUT_OF_RESOURCES
;
299 Status
= gMmst
->MmLocateHandle (
301 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
307 *NumberHandles
= BufferSize
/ sizeof(EFI_HANDLE
);
308 if (EFI_ERROR(Status
)) {
319 Get the variable statistics information from the information buffer pointed by gVariableInfo.
321 Caution: This function may be invoked at SMM runtime.
322 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
324 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
325 On input, point to the variable information returned last time. if
326 InfoEntry->VendorGuid is zero, return the first information.
327 On output, point to the next variable information.
328 @param[in, out] InfoSize On input, the size of the variable information buffer.
329 On output, the returned variable information size.
331 @retval EFI_SUCCESS The variable information is found and returned successfully.
332 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
333 PcdVariableCollectStatistics should be set TRUE to support it.
334 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
335 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
339 SmmVariableGetStatistics (
340 IN OUT VARIABLE_INFO_ENTRY
*InfoEntry
,
341 IN OUT UINTN
*InfoSize
344 VARIABLE_INFO_ENTRY
*VariableInfo
;
346 UINTN StatisticsInfoSize
;
348 UINTN InfoNameMaxSize
;
351 if (InfoEntry
== NULL
) {
352 return EFI_INVALID_PARAMETER
;
355 VariableInfo
= gVariableInfo
;
356 if (VariableInfo
== NULL
) {
357 return EFI_UNSUPPORTED
;
360 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
);
361 if (*InfoSize
< StatisticsInfoSize
) {
362 *InfoSize
= StatisticsInfoSize
;
363 return EFI_BUFFER_TOO_SMALL
;
365 InfoName
= (CHAR16
*)(InfoEntry
+ 1);
366 InfoNameMaxSize
= (*InfoSize
- sizeof (VARIABLE_INFO_ENTRY
));
368 CopyGuid (&VendorGuid
, &InfoEntry
->VendorGuid
);
370 if (IsZeroGuid (&VendorGuid
)) {
372 // Return the first variable info
374 NameSize
= StrSize (VariableInfo
->Name
);
375 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
376 if (*InfoSize
< StatisticsInfoSize
) {
377 *InfoSize
= StatisticsInfoSize
;
378 return EFI_BUFFER_TOO_SMALL
;
380 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
381 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
382 *InfoSize
= StatisticsInfoSize
;
387 // Get the next variable info
389 while (VariableInfo
!= NULL
) {
390 if (CompareGuid (&VariableInfo
->VendorGuid
, &VendorGuid
)) {
391 NameSize
= StrSize (VariableInfo
->Name
);
392 if (NameSize
<= InfoNameMaxSize
) {
393 if (CompareMem (VariableInfo
->Name
, InfoName
, NameSize
) == 0) {
395 // Find the match one
397 VariableInfo
= VariableInfo
->Next
;
402 VariableInfo
= VariableInfo
->Next
;
405 if (VariableInfo
== NULL
) {
411 // Output the new variable info
413 NameSize
= StrSize (VariableInfo
->Name
);
414 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
415 if (*InfoSize
< StatisticsInfoSize
) {
416 *InfoSize
= StatisticsInfoSize
;
417 return EFI_BUFFER_TOO_SMALL
;
420 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
421 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
422 *InfoSize
= StatisticsInfoSize
;
429 Communication service SMI Handler entry.
431 This SMI handler provides services for the variable wrapper driver.
433 Caution: This function may receive untrusted input.
434 This variable data and communicate buffer are external input, so this function will do basic validation.
435 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
436 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
437 SmmVariableGetStatistics() should also do validation based on its own knowledge.
439 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
440 @param[in] RegisterContext Points to an optional handler context which was specified when the
441 handler was registered.
442 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
443 be conveyed from a non-SMM environment into an SMM environment.
444 @param[in, out] CommBufferSize The size of the CommBuffer.
446 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
447 should still be called.
448 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
450 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
452 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
457 IN EFI_HANDLE DispatchHandle
,
458 IN CONST VOID
*RegisterContext
,
459 IN OUT VOID
*CommBuffer
,
460 IN OUT UINTN
*CommBufferSize
464 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
465 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
466 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*GetNextVariableName
;
467 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*QueryVariableInfo
;
468 SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
*GetPayloadSize
;
469 SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
*RuntimeVariableCacheContext
;
470 SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
*GetRuntimeCacheInfo
;
471 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*VariableToLock
;
472 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*CommVariableProperty
;
473 VARIABLE_INFO_ENTRY
*VariableInfo
;
474 VARIABLE_RUNTIME_CACHE_CONTEXT
*VariableCacheContext
;
475 VARIABLE_STORE_HEADER
*VariableCache
;
477 UINTN NameBufferSize
;
478 UINTN CommBufferPayloadSize
;
479 UINTN TempCommBufferSize
;
482 // If input is invalid, stop processing this SMI
484 if (CommBuffer
== NULL
|| CommBufferSize
== NULL
) {
488 TempCommBufferSize
= *CommBufferSize
;
490 if (TempCommBufferSize
< SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
) {
491 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
494 CommBufferPayloadSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
495 if (CommBufferPayloadSize
> mVariableBufferPayloadSize
) {
496 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
500 if (!VariableSmmIsBufferOutsideSmmValid ((UINTN
)CommBuffer
, TempCommBufferSize
)) {
501 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
505 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)CommBuffer
;
506 switch (SmmVariableFunctionHeader
->Function
) {
507 case SMM_VARIABLE_FUNCTION_GET_VARIABLE
:
508 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
509 DEBUG ((EFI_D_ERROR
, "GetVariable: SMM communication buffer size invalid!\n"));
513 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
515 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
516 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
517 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
518 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
520 // Prevent InfoSize overflow happen
522 Status
= EFI_ACCESS_DENIED
;
525 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
526 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
529 // SMRAM range check already covered before
531 if (InfoSize
> CommBufferPayloadSize
) {
532 DEBUG ((EFI_D_ERROR
, "GetVariable: Data size exceed communication buffer size limit!\n"));
533 Status
= EFI_ACCESS_DENIED
;
538 // The VariableSpeculationBarrier() call here is to ensure the previous
539 // range/content checks for the CommBuffer have been completed before the
540 // subsequent consumption of the CommBuffer content.
542 VariableSpeculationBarrier ();
543 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
545 // Make sure VariableName is A Null-terminated string.
547 Status
= EFI_ACCESS_DENIED
;
551 Status
= VariableServiceGetVariable (
552 SmmVariableHeader
->Name
,
553 &SmmVariableHeader
->Guid
,
554 &SmmVariableHeader
->Attributes
,
555 &SmmVariableHeader
->DataSize
,
556 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
558 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
561 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
:
562 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
563 DEBUG ((EFI_D_ERROR
, "GetNextVariableName: SMM communication buffer size invalid!\n"));
567 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
569 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
570 GetNextVariableName
= (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*) mVariableBufferPayload
;
571 if ((UINTN
)(~0) - GetNextVariableName
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
573 // Prevent InfoSize overflow happen
575 Status
= EFI_ACCESS_DENIED
;
578 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + GetNextVariableName
->NameSize
;
581 // SMRAM range check already covered before
583 if (InfoSize
> CommBufferPayloadSize
) {
584 DEBUG ((EFI_D_ERROR
, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
585 Status
= EFI_ACCESS_DENIED
;
589 NameBufferSize
= CommBufferPayloadSize
- OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
);
590 if (NameBufferSize
< sizeof (CHAR16
) || GetNextVariableName
->Name
[NameBufferSize
/sizeof (CHAR16
) - 1] != L
'\0') {
592 // Make sure input VariableName is A Null-terminated string.
594 Status
= EFI_ACCESS_DENIED
;
598 Status
= VariableServiceGetNextVariableName (
599 &GetNextVariableName
->NameSize
,
600 GetNextVariableName
->Name
,
601 &GetNextVariableName
->Guid
603 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
606 case SMM_VARIABLE_FUNCTION_SET_VARIABLE
:
607 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
608 DEBUG ((EFI_D_ERROR
, "SetVariable: SMM communication buffer size invalid!\n"));
612 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
614 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
615 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
616 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
617 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
619 // Prevent InfoSize overflow happen
621 Status
= EFI_ACCESS_DENIED
;
624 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
625 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
628 // SMRAM range check already covered before
629 // Data buffer should not contain SMM range
631 if (InfoSize
> CommBufferPayloadSize
) {
632 DEBUG ((EFI_D_ERROR
, "SetVariable: Data size exceed communication buffer size limit!\n"));
633 Status
= EFI_ACCESS_DENIED
;
638 // The VariableSpeculationBarrier() call here is to ensure the previous
639 // range/content checks for the CommBuffer have been completed before the
640 // subsequent consumption of the CommBuffer content.
642 VariableSpeculationBarrier ();
643 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
645 // Make sure VariableName is A Null-terminated string.
647 Status
= EFI_ACCESS_DENIED
;
651 Status
= VariableServiceSetVariable (
652 SmmVariableHeader
->Name
,
653 &SmmVariableHeader
->Guid
,
654 SmmVariableHeader
->Attributes
,
655 SmmVariableHeader
->DataSize
,
656 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
660 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
:
661 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
)) {
662 DEBUG ((EFI_D_ERROR
, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
665 QueryVariableInfo
= (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*) SmmVariableFunctionHeader
->Data
;
667 Status
= VariableServiceQueryVariableInfo (
668 QueryVariableInfo
->Attributes
,
669 &QueryVariableInfo
->MaximumVariableStorageSize
,
670 &QueryVariableInfo
->RemainingVariableStorageSize
,
671 &QueryVariableInfo
->MaximumVariableSize
675 case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE
:
676 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
)) {
677 DEBUG ((EFI_D_ERROR
, "GetPayloadSize: SMM communication buffer size invalid!\n"));
680 GetPayloadSize
= (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
*) SmmVariableFunctionHeader
->Data
;
681 GetPayloadSize
->VariablePayloadSize
= mVariableBufferPayloadSize
;
682 Status
= EFI_SUCCESS
;
685 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT
:
687 Status
= EFI_UNSUPPORTED
;
691 MorLockInitAtEndOfDxe ();
693 VarCheckLibInitializeAtEndOfDxe (NULL
);
695 // The initialization for variable quota.
697 InitializeVariableQuota ();
700 Status
= EFI_SUCCESS
;
703 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
:
705 Status
= EFI_SUCCESS
;
708 case SMM_VARIABLE_FUNCTION_GET_STATISTICS
:
709 VariableInfo
= (VARIABLE_INFO_ENTRY
*) SmmVariableFunctionHeader
->Data
;
710 InfoSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
713 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
714 // It is covered by previous CommBuffer check
718 // Do not need to check CommBufferSize buffer as it should point to SMRAM
719 // that was used by SMM core to cache CommSize from SmmCommunication protocol.
722 Status
= SmmVariableGetStatistics (VariableInfo
, &InfoSize
);
723 *CommBufferSize
= InfoSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
726 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE
:
728 Status
= EFI_ACCESS_DENIED
;
730 VariableToLock
= (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*) SmmVariableFunctionHeader
->Data
;
731 Status
= VariableLockRequestToLock (
733 VariableToLock
->Name
,
734 &VariableToLock
->Guid
738 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET
:
740 Status
= EFI_ACCESS_DENIED
;
742 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) SmmVariableFunctionHeader
->Data
;
743 Status
= VarCheckVariablePropertySet (
744 CommVariableProperty
->Name
,
745 &CommVariableProperty
->Guid
,
746 &CommVariableProperty
->VariableProperty
750 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET
:
751 if (CommBufferPayloadSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
752 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
756 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
758 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
759 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) mVariableBufferPayload
;
760 if ((UINTN
) (~0) - CommVariableProperty
->NameSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
762 // Prevent InfoSize overflow happen
764 Status
= EFI_ACCESS_DENIED
;
767 InfoSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) + CommVariableProperty
->NameSize
;
770 // SMRAM range check already covered before
772 if (InfoSize
> CommBufferPayloadSize
) {
773 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
774 Status
= EFI_ACCESS_DENIED
;
779 // The VariableSpeculationBarrier() call here is to ensure the previous
780 // range/content checks for the CommBuffer have been completed before the
781 // subsequent consumption of the CommBuffer content.
783 VariableSpeculationBarrier ();
784 if (CommVariableProperty
->NameSize
< sizeof (CHAR16
) || CommVariableProperty
->Name
[CommVariableProperty
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
786 // Make sure VariableName is A Null-terminated string.
788 Status
= EFI_ACCESS_DENIED
;
792 Status
= VarCheckVariablePropertyGet (
793 CommVariableProperty
->Name
,
794 &CommVariableProperty
->Guid
,
795 &CommVariableProperty
->VariableProperty
797 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
799 case SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT
:
800 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
)) {
801 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: SMM communication buffer size invalid!\n"));
802 Status
= EFI_ACCESS_DENIED
;
806 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Cannot init context after end of DXE!\n"));
807 Status
= EFI_ACCESS_DENIED
;
812 // Copy the input communicate buffer payload to the pre-allocated SMM variable payload buffer.
814 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
815 RuntimeVariableCacheContext
= (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT
*) mVariableBufferPayload
;
818 // Verify required runtime cache buffers are provided.
820 if (RuntimeVariableCacheContext
->RuntimeVolatileCache
== NULL
||
821 RuntimeVariableCacheContext
->RuntimeNvCache
== NULL
||
822 RuntimeVariableCacheContext
->PendingUpdate
== NULL
||
823 RuntimeVariableCacheContext
->ReadLock
== NULL
||
824 RuntimeVariableCacheContext
->HobFlushComplete
== NULL
) {
825 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Required runtime cache buffer is NULL!\n"));
826 Status
= EFI_ACCESS_DENIED
;
831 // Verify minimum size requirements for the runtime variable store buffers.
833 if ((RuntimeVariableCacheContext
->RuntimeHobCache
!= NULL
&&
834 RuntimeVariableCacheContext
->RuntimeHobCache
->Size
< sizeof (VARIABLE_STORE_HEADER
)) ||
835 RuntimeVariableCacheContext
->RuntimeVolatileCache
->Size
< sizeof (VARIABLE_STORE_HEADER
) ||
836 RuntimeVariableCacheContext
->RuntimeNvCache
->Size
< sizeof (VARIABLE_STORE_HEADER
)) {
837 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: A runtime cache buffer size is invalid!\n"));
838 Status
= EFI_ACCESS_DENIED
;
843 // Verify runtime buffers do not overlap with SMRAM ranges.
845 if (RuntimeVariableCacheContext
->RuntimeHobCache
!= NULL
&&
846 !VariableSmmIsBufferOutsideSmmValid (
847 (UINTN
) RuntimeVariableCacheContext
->RuntimeHobCache
,
848 (UINTN
) RuntimeVariableCacheContext
->RuntimeHobCache
->Size
)) {
849 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime HOB cache buffer in SMRAM or overflow!\n"));
850 Status
= EFI_ACCESS_DENIED
;
853 if (!VariableSmmIsBufferOutsideSmmValid (
854 (UINTN
) RuntimeVariableCacheContext
->RuntimeVolatileCache
,
855 (UINTN
) RuntimeVariableCacheContext
->RuntimeVolatileCache
->Size
)) {
856 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime volatile cache buffer in SMRAM or overflow!\n"));
857 Status
= EFI_ACCESS_DENIED
;
860 if (!VariableSmmIsBufferOutsideSmmValid (
861 (UINTN
) RuntimeVariableCacheContext
->RuntimeNvCache
,
862 (UINTN
) RuntimeVariableCacheContext
->RuntimeNvCache
->Size
)) {
863 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime non-volatile cache buffer in SMRAM or overflow!\n"));
864 Status
= EFI_ACCESS_DENIED
;
867 if (!VariableSmmIsBufferOutsideSmmValid (
868 (UINTN
) RuntimeVariableCacheContext
->PendingUpdate
,
869 sizeof (*(RuntimeVariableCacheContext
->PendingUpdate
)))) {
870 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache pending update buffer in SMRAM or overflow!\n"));
871 Status
= EFI_ACCESS_DENIED
;
874 if (!VariableSmmIsBufferOutsideSmmValid (
875 (UINTN
) RuntimeVariableCacheContext
->ReadLock
,
876 sizeof (*(RuntimeVariableCacheContext
->ReadLock
)))) {
877 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache read lock buffer in SMRAM or overflow!\n"));
878 Status
= EFI_ACCESS_DENIED
;
881 if (!VariableSmmIsBufferOutsideSmmValid (
882 (UINTN
) RuntimeVariableCacheContext
->HobFlushComplete
,
883 sizeof (*(RuntimeVariableCacheContext
->HobFlushComplete
)))) {
884 DEBUG ((DEBUG_ERROR
, "InitRuntimeVariableCacheContext: Runtime cache HOB flush complete buffer in SMRAM or overflow!\n"));
885 Status
= EFI_ACCESS_DENIED
;
889 VariableCacheContext
= &mVariableModuleGlobal
->VariableGlobal
.VariableRuntimeCacheContext
;
890 VariableCacheContext
->VariableRuntimeHobCache
.Store
= RuntimeVariableCacheContext
->RuntimeHobCache
;
891 VariableCacheContext
->VariableRuntimeVolatileCache
.Store
= RuntimeVariableCacheContext
->RuntimeVolatileCache
;
892 VariableCacheContext
->VariableRuntimeNvCache
.Store
= RuntimeVariableCacheContext
->RuntimeNvCache
;
893 VariableCacheContext
->PendingUpdate
= RuntimeVariableCacheContext
->PendingUpdate
;
894 VariableCacheContext
->ReadLock
= RuntimeVariableCacheContext
->ReadLock
;
895 VariableCacheContext
->HobFlushComplete
= RuntimeVariableCacheContext
->HobFlushComplete
;
897 // Set up the intial pending request since the RT cache needs to be in sync with SMM cache
898 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateOffset
= 0;
899 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateLength
= 0;
900 if (mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
> 0 &&
901 VariableCacheContext
->VariableRuntimeHobCache
.Store
!= NULL
) {
902 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
;
903 VariableCacheContext
->VariableRuntimeHobCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
904 CopyGuid (&(VariableCacheContext
->VariableRuntimeHobCache
.Store
->Signature
), &(VariableCache
->Signature
));
906 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.VolatileVariableBase
;
907 VariableCacheContext
->VariableRuntimeVolatileCache
.PendingUpdateOffset
= 0;
908 VariableCacheContext
->VariableRuntimeVolatileCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
909 CopyGuid (&(VariableCacheContext
->VariableRuntimeVolatileCache
.Store
->Signature
), &(VariableCache
->Signature
));
911 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mNvVariableCache
;
912 VariableCacheContext
->VariableRuntimeNvCache
.PendingUpdateOffset
= 0;
913 VariableCacheContext
->VariableRuntimeNvCache
.PendingUpdateLength
= (UINT32
) ((UINTN
) GetEndPointer (VariableCache
) - (UINTN
) VariableCache
);
914 CopyGuid (&(VariableCacheContext
->VariableRuntimeNvCache
.Store
->Signature
), &(VariableCache
->Signature
));
916 *(VariableCacheContext
->PendingUpdate
) = TRUE
;
917 *(VariableCacheContext
->ReadLock
) = FALSE
;
918 *(VariableCacheContext
->HobFlushComplete
) = FALSE
;
920 Status
= EFI_SUCCESS
;
922 case SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE
:
923 Status
= FlushPendingRuntimeVariableCacheUpdates ();
925 case SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO
:
926 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
)) {
927 DEBUG ((DEBUG_ERROR
, "GetRuntimeCacheInfo: SMM communication buffer size invalid!\n"));
930 GetRuntimeCacheInfo
= (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO
*) SmmVariableFunctionHeader
->Data
;
932 if (mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
> 0) {
933 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.HobVariableBase
;
934 GetRuntimeCacheInfo
->TotalHobStorageSize
= VariableCache
->Size
;
936 GetRuntimeCacheInfo
->TotalHobStorageSize
= 0;
939 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mVariableModuleGlobal
->VariableGlobal
.VolatileVariableBase
;
940 GetRuntimeCacheInfo
->TotalVolatileStorageSize
= VariableCache
->Size
;
941 VariableCache
= (VARIABLE_STORE_HEADER
*) (UINTN
) mNvVariableCache
;
942 GetRuntimeCacheInfo
->TotalNvStorageSize
= (UINTN
) VariableCache
->Size
;
943 GetRuntimeCacheInfo
->AuthenticatedVariableUsage
= mVariableModuleGlobal
->VariableGlobal
.AuthFormat
;
945 Status
= EFI_SUCCESS
;
949 Status
= EFI_UNSUPPORTED
;
954 SmmVariableFunctionHeader
->ReturnStatus
= Status
;
960 SMM END_OF_DXE protocol notification event handler.
962 @param Protocol Points to the protocol's unique identifier
963 @param Interface Points to the interface instance
964 @param Handle The handle on which the interface was installed
966 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
971 SmmEndOfDxeCallback (
972 IN CONST EFI_GUID
*Protocol
,
977 DEBUG ((EFI_D_INFO
, "[Variable]SMM_END_OF_DXE is signaled\n"));
978 MorLockInitAtEndOfDxe ();
980 VarCheckLibInitializeAtEndOfDxe (NULL
);
982 // The initialization for variable quota.
984 InitializeVariableQuota ();
985 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe
)) {
993 Initializes variable write service for SMM.
997 VariableWriteServiceInitializeSmm (
1003 Status
= VariableWriteServiceInitialize ();
1004 if (EFI_ERROR (Status
)) {
1005 DEBUG ((DEBUG_ERROR
, "Variable write service initialization failed. Status = %r\n", Status
));
1009 // Notify the variable wrapper driver the variable write service is ready
1011 VariableNotifySmmWriteReady ();
1015 SMM Fault Tolerant Write protocol notification event handler.
1017 Non-Volatile variable write may needs FTW protocol to reclaim when
1020 @param Protocol Points to the protocol's unique identifier
1021 @param Interface Points to the interface instance
1022 @param Handle The handle on which the interface was installed
1024 @retval EFI_SUCCESS SmmEventCallback runs successfully
1025 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
1030 SmmFtwNotificationEvent (
1031 IN CONST EFI_GUID
*Protocol
,
1033 IN EFI_HANDLE Handle
1037 EFI_PHYSICAL_ADDRESS VariableStoreBase
;
1038 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvbProtocol
;
1039 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL
*FtwProtocol
;
1040 EFI_PHYSICAL_ADDRESS NvStorageVariableBase
;
1041 UINTN FtwMaxBlockSize
;
1043 if (mVariableModuleGlobal
->FvbInstance
!= NULL
) {
1048 // Ensure SMM FTW protocol is installed.
1050 Status
= GetFtwProtocol ((VOID
**)&FtwProtocol
);
1051 if (EFI_ERROR (Status
)) {
1055 Status
= FtwProtocol
->GetMaxBlockSize (FtwProtocol
, &FtwMaxBlockSize
);
1056 if (!EFI_ERROR (Status
)) {
1057 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize
) <= FtwMaxBlockSize
);
1060 NvStorageVariableBase
= NV_STORAGE_VARIABLE_BASE
;
1061 VariableStoreBase
= NvStorageVariableBase
+ mNvFvHeaderCache
->HeaderLength
;
1064 // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready.
1066 mVariableModuleGlobal
->VariableGlobal
.NonVolatileVariableBase
= VariableStoreBase
;
1069 // Find the proper FVB protocol for variable.
1071 Status
= GetFvbInfoByAddress (NvStorageVariableBase
, NULL
, &FvbProtocol
);
1072 if (EFI_ERROR (Status
)) {
1073 return EFI_NOT_FOUND
;
1076 mVariableModuleGlobal
->FvbInstance
= FvbProtocol
;
1079 // Initializes variable write service after FTW was ready.
1081 VariableWriteServiceInitializeSmm ();
1088 Variable Driver main entry point. The Variable driver places the 4 EFI
1089 runtime services in the EFI System Table and installs arch protocols
1090 for variable read and write services being available. It also registers
1091 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
1093 @retval EFI_SUCCESS Variable service successfully initialized.
1098 MmVariableServiceInitialize (
1103 EFI_HANDLE VariableHandle
;
1104 VOID
*SmmFtwRegistration
;
1105 VOID
*SmmEndOfDxeRegistration
;
1108 // Variable initialize.
1110 Status
= VariableCommonInitialize ();
1111 ASSERT_EFI_ERROR (Status
);
1114 // Install the Smm Variable Protocol on a new handle.
1116 VariableHandle
= NULL
;
1117 Status
= gMmst
->MmInstallProtocolInterface (
1119 &gEfiSmmVariableProtocolGuid
,
1120 EFI_NATIVE_INTERFACE
,
1123 ASSERT_EFI_ERROR (Status
);
1125 Status
= gMmst
->MmInstallProtocolInterface (
1127 &gEdkiiSmmVarCheckProtocolGuid
,
1128 EFI_NATIVE_INTERFACE
,
1131 ASSERT_EFI_ERROR (Status
);
1133 mVariableBufferPayloadSize
= GetMaxVariableSize () +
1134 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) -
1135 GetVariableHeaderSize (mVariableModuleGlobal
->VariableGlobal
.AuthFormat
);
1137 Status
= gMmst
->MmAllocatePool (
1138 EfiRuntimeServicesData
,
1139 mVariableBufferPayloadSize
,
1140 (VOID
**)&mVariableBufferPayload
1142 ASSERT_EFI_ERROR (Status
);
1145 /// Register SMM variable SMI handler
1147 VariableHandle
= NULL
;
1148 Status
= gMmst
->MmiHandlerRegister (SmmVariableHandler
, &gEfiSmmVariableProtocolGuid
, &VariableHandle
);
1149 ASSERT_EFI_ERROR (Status
);
1152 // Notify the variable wrapper driver the variable service is ready
1154 VariableNotifySmmReady ();
1157 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
1159 Status
= gMmst
->MmRegisterProtocolNotify (
1160 &gEfiMmEndOfDxeProtocolGuid
,
1161 SmmEndOfDxeCallback
,
1162 &SmmEndOfDxeRegistration
1164 ASSERT_EFI_ERROR (Status
);
1166 if (!PcdGetBool (PcdEmuVariableNvModeEnable
)) {
1168 // Register FtwNotificationEvent () notify function.
1170 Status
= gMmst
->MmRegisterProtocolNotify (
1171 &gEfiSmmFaultTolerantWriteProtocolGuid
,
1172 SmmFtwNotificationEvent
,
1175 ASSERT_EFI_ERROR (Status
);
1177 SmmFtwNotificationEvent (NULL
, NULL
, NULL
);
1180 // Emulated non-volatile variable mode does not depend on FVB and FTW.
1182 VariableWriteServiceInitializeSmm ();