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 - 2015, Intel Corporation. All rights reserved.<BR>
18 This program and the accompanying materials
19 are licensed and made available under the terms and conditions of the BSD License
20 which accompanies this distribution. The full text of the license may be found at
21 http://opensource.org/licenses/bsd-license.php
23 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
24 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
28 #include <Protocol/SmmVariable.h>
29 #include <Protocol/SmmFirmwareVolumeBlock.h>
30 #include <Protocol/SmmFaultTolerantWrite.h>
31 #include <Protocol/SmmAccess2.h>
32 #include <Protocol/SmmEndOfDxe.h>
33 #include <Protocol/SmmVarCheck.h>
35 #include <Library/SmmServicesTableLib.h>
37 #include <Guid/AuthenticatedVariableFormat.h>
38 #include <Guid/SmmVariableCommon.h>
41 EFI_SMRAM_DESCRIPTOR
*mSmramRanges
;
42 UINTN mSmramRangeCount
;
44 extern VARIABLE_INFO_ENTRY
*gVariableInfo
;
45 EFI_HANDLE mSmmVariableHandle
= NULL
;
46 EFI_HANDLE mVariableHandle
= NULL
;
47 BOOLEAN mAtRuntime
= FALSE
;
48 EFI_GUID mZeroGuid
= {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
49 UINT8
*mVariableBufferPayload
= NULL
;
50 UINTN mVariableBufferPayloadSize
;
51 extern BOOLEAN mEndOfDxe
;
52 extern BOOLEAN mEnableLocking
;
55 SecureBoot Hook for SetVariable.
57 @param[in] VariableName Name of Variable to be found.
58 @param[in] VendorGuid Variable vendor GUID.
64 IN CHAR16
*VariableName
,
65 IN EFI_GUID
*VendorGuid
73 This code sets variable in storage blocks (Volatile or Non-Volatile).
75 @param VariableName Name of Variable to be found.
76 @param VendorGuid Variable vendor GUID.
77 @param Attributes Attribute value of the variable found
78 @param DataSize Size of Data found. If size is less than the
79 data, this value contains the required size.
80 @param Data Data pointer.
82 @return EFI_INVALID_PARAMETER Invalid parameter.
83 @return EFI_SUCCESS Set successfully.
84 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
85 @return EFI_NOT_FOUND Not found.
86 @return EFI_WRITE_PROTECTED Variable is read-only.
91 SmmVariableSetVariable (
92 IN CHAR16
*VariableName
,
93 IN EFI_GUID
*VendorGuid
,
102 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
104 mEnableLocking
= FALSE
;
105 Status
= VariableServiceSetVariable (
112 mEnableLocking
= TRUE
;
116 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable
= {
117 VariableServiceGetVariable
,
118 VariableServiceGetNextVariableName
,
119 SmmVariableSetVariable
,
120 VariableServiceQueryVariableInfo
123 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck
= { VarCheckRegisterSetVariableCheckHandler
,
124 VarCheckVariablePropertySet
,
125 VarCheckVariablePropertyGet
};
128 Return TRUE if ExitBootServices () has been called.
130 @retval TRUE If ExitBootServices () has been called.
141 This function check if the address is in SMRAM.
143 @param Buffer the buffer address to be checked.
144 @param Length the buffer length to be checked.
146 @retval TRUE this address is in SMRAM.
147 @retval FALSE this address is NOT in SMRAM.
150 InternalIsAddressInSmram (
151 IN EFI_PHYSICAL_ADDRESS Buffer
,
157 for (Index
= 0; Index
< mSmramRangeCount
; Index
++) {
158 if (((Buffer
>= mSmramRanges
[Index
].CpuStart
) && (Buffer
< mSmramRanges
[Index
].CpuStart
+ mSmramRanges
[Index
].PhysicalSize
)) ||
159 ((mSmramRanges
[Index
].CpuStart
>= Buffer
) && (mSmramRanges
[Index
].CpuStart
< Buffer
+ Length
))) {
168 This function check if the address refered by Buffer and Length is valid.
170 @param Buffer the buffer address to be checked.
171 @param Length the buffer length to be checked.
173 @retval TRUE this address is valid.
174 @retval FALSE this address is NOT valid.
177 InternalIsAddressValid (
182 if (Buffer
> (MAX_ADDRESS
- Length
)) {
188 if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS
)Buffer
, (UINT64
)Length
)) {
195 Initializes a basic mutual exclusion lock.
197 This function initializes a basic mutual exclusion lock to the released state
198 and returns the lock. Each lock provides mutual exclusion access at its task
199 priority level. Since there is no preemption or multiprocessor support in EFI,
200 acquiring the lock only consists of raising to the locks TPL.
201 If Lock is NULL, then ASSERT().
202 If Priority is not a valid TPL value, then ASSERT().
204 @param Lock A pointer to the lock data structure to initialize.
205 @param Priority EFI TPL is associated with the lock.
212 IN OUT EFI_LOCK
*Lock
,
220 Acquires lock only at boot time. Simply returns at runtime.
222 This is a temperary function that will be removed when
223 EfiAcquireLock() in UefiLib can handle the call in UEFI
224 Runtimer driver in RT phase.
225 It calls EfiAcquireLock() at boot time, and simply returns
228 @param Lock A pointer to the lock to acquire.
232 AcquireLockOnlyAtBootTime (
241 Releases lock only at boot time. Simply returns at runtime.
243 This is a temperary function which will be removed when
244 EfiReleaseLock() in UefiLib can handle the call in UEFI
245 Runtimer driver in RT phase.
246 It calls EfiReleaseLock() at boot time and simply returns
249 @param Lock A pointer to the lock to release.
253 ReleaseLockOnlyAtBootTime (
261 Retrive the SMM Fault Tolerent Write protocol interface.
263 @param[out] FtwProtocol The interface of SMM Ftw protocol
265 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
266 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
267 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
272 OUT VOID
**FtwProtocol
278 // Locate Smm Fault Tolerent Write protocol
280 Status
= gSmst
->SmmLocateProtocol (
281 &gEfiSmmFaultTolerantWriteProtocolGuid
,
290 Retrive the SMM FVB protocol interface by HANDLE.
292 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
293 reading, writing, and erasing the target block.
294 @param[out] FvBlock The interface of SMM FVB protocol
296 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
297 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
298 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
303 IN EFI_HANDLE FvBlockHandle
,
304 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
308 // To get the SMM FVB protocol interface on the handle
310 return gSmst
->SmmHandleProtocol (
312 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
319 Function returns an array of handles that support the SMM FVB protocol
320 in a buffer allocated from pool.
322 @param[out] NumberHandles The number of handles returned in Buffer.
323 @param[out] Buffer A pointer to the buffer to return the requested
324 array of handles that support SMM FVB protocol.
326 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
327 handles in Buffer was returned in NumberHandles.
328 @retval EFI_NOT_FOUND No SMM FVB handle was found.
329 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
330 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
334 GetFvbCountAndBuffer (
335 OUT UINTN
*NumberHandles
,
336 OUT EFI_HANDLE
**Buffer
342 if ((NumberHandles
== NULL
) || (Buffer
== NULL
)) {
343 return EFI_INVALID_PARAMETER
;
349 Status
= gSmst
->SmmLocateHandle (
351 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
356 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
357 return EFI_NOT_FOUND
;
360 *Buffer
= AllocatePool (BufferSize
);
361 if (*Buffer
== NULL
) {
362 return EFI_OUT_OF_RESOURCES
;
365 Status
= gSmst
->SmmLocateHandle (
367 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
373 *NumberHandles
= BufferSize
/ sizeof(EFI_HANDLE
);
374 if (EFI_ERROR(Status
)) {
385 Get the variable statistics information from the information buffer pointed by gVariableInfo.
387 Caution: This function may be invoked at SMM runtime.
388 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
390 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
391 On input, point to the variable information returned last time. if
392 InfoEntry->VendorGuid is zero, return the first information.
393 On output, point to the next variable information.
394 @param[in, out] InfoSize On input, the size of the variable information buffer.
395 On output, the returned variable information size.
397 @retval EFI_SUCCESS The variable information is found and returned successfully.
398 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
399 PcdVariableCollectStatistics should be set TRUE to support it.
400 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
401 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
405 SmmVariableGetStatistics (
406 IN OUT VARIABLE_INFO_ENTRY
*InfoEntry
,
407 IN OUT UINTN
*InfoSize
410 VARIABLE_INFO_ENTRY
*VariableInfo
;
412 UINTN StatisticsInfoSize
;
416 if (InfoEntry
== NULL
) {
417 return EFI_INVALID_PARAMETER
;
420 VariableInfo
= gVariableInfo
;
421 if (VariableInfo
== NULL
) {
422 return EFI_UNSUPPORTED
;
425 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + StrSize (VariableInfo
->Name
);
426 if (*InfoSize
< StatisticsInfoSize
) {
427 *InfoSize
= StatisticsInfoSize
;
428 return EFI_BUFFER_TOO_SMALL
;
430 InfoName
= (CHAR16
*)(InfoEntry
+ 1);
432 CopyGuid (&VendorGuid
, &InfoEntry
->VendorGuid
);
434 if (CompareGuid (&VendorGuid
, &mZeroGuid
)) {
436 // Return the first variable info
438 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
439 CopyMem (InfoName
, VariableInfo
->Name
, StrSize (VariableInfo
->Name
));
440 *InfoSize
= StatisticsInfoSize
;
445 // Get the next variable info
447 while (VariableInfo
!= NULL
) {
448 if (CompareGuid (&VariableInfo
->VendorGuid
, &VendorGuid
)) {
449 NameLength
= StrSize (VariableInfo
->Name
);
450 if (NameLength
== StrSize (InfoName
)) {
451 if (CompareMem (VariableInfo
->Name
, InfoName
, NameLength
) == 0) {
453 // Find the match one
455 VariableInfo
= VariableInfo
->Next
;
460 VariableInfo
= VariableInfo
->Next
;
463 if (VariableInfo
== NULL
) {
469 // Output the new variable info
471 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + StrSize (VariableInfo
->Name
);
472 if (*InfoSize
< StatisticsInfoSize
) {
473 *InfoSize
= StatisticsInfoSize
;
474 return EFI_BUFFER_TOO_SMALL
;
477 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
478 CopyMem (InfoName
, VariableInfo
->Name
, StrSize (VariableInfo
->Name
));
479 *InfoSize
= StatisticsInfoSize
;
486 Communication service SMI Handler entry.
488 This SMI handler provides services for the variable wrapper driver.
490 Caution: This function may receive untrusted input.
491 This variable data and communicate buffer are external input, so this function will do basic validation.
492 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
493 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
494 SmmVariableGetStatistics() should also do validation based on its own knowledge.
496 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
497 @param[in] RegisterContext Points to an optional handler context which was specified when the
498 handler was registered.
499 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
500 be conveyed from a non-SMM environment into an SMM environment.
501 @param[in, out] CommBufferSize The size of the CommBuffer.
503 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
504 should still be called.
505 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
507 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
509 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
515 IN EFI_HANDLE DispatchHandle
,
516 IN CONST VOID
*RegisterContext
,
517 IN OUT VOID
*CommBuffer
,
518 IN OUT UINTN
*CommBufferSize
522 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
523 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
524 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*GetNextVariableName
;
525 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*QueryVariableInfo
;
526 VARIABLE_INFO_ENTRY
*VariableInfo
;
527 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*VariableToLock
;
528 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*CommVariableProperty
;
530 UINTN NameBufferSize
;
531 UINTN CommBufferPayloadSize
;
532 UINTN TempCommBufferSize
;
535 // If input is invalid, stop processing this SMI
537 if (CommBuffer
== NULL
|| CommBufferSize
== NULL
) {
541 TempCommBufferSize
= *CommBufferSize
;
543 if (TempCommBufferSize
< SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
) {
544 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
547 CommBufferPayloadSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
548 if (CommBufferPayloadSize
> mVariableBufferPayloadSize
) {
549 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
553 if (!InternalIsAddressValid ((UINTN
)CommBuffer
, TempCommBufferSize
)) {
554 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
558 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)CommBuffer
;
560 switch (SmmVariableFunctionHeader
->Function
) {
561 case SMM_VARIABLE_FUNCTION_GET_VARIABLE
:
562 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
563 DEBUG ((EFI_D_ERROR
, "GetVariable: 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 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
571 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
572 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
574 // Prevent InfoSize overflow happen
576 Status
= EFI_ACCESS_DENIED
;
579 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
580 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
583 // SMRAM range check already covered before
585 if (InfoSize
> CommBufferPayloadSize
) {
586 DEBUG ((EFI_D_ERROR
, "GetVariable: Data size exceed communication buffer size limit!\n"));
587 Status
= EFI_ACCESS_DENIED
;
591 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
593 // Make sure VariableName is A Null-terminated string.
595 Status
= EFI_ACCESS_DENIED
;
599 Status
= VariableServiceGetVariable (
600 SmmVariableHeader
->Name
,
601 &SmmVariableHeader
->Guid
,
602 &SmmVariableHeader
->Attributes
,
603 &SmmVariableHeader
->DataSize
,
604 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
606 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
609 case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
:
610 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
611 DEBUG ((EFI_D_ERROR
, "GetNextVariableName: SMM communication buffer size invalid!\n"));
615 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
617 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
618 GetNextVariableName
= (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*) mVariableBufferPayload
;
619 if ((UINTN
)(~0) - GetNextVariableName
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
)) {
621 // Prevent InfoSize overflow happen
623 Status
= EFI_ACCESS_DENIED
;
626 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + GetNextVariableName
->NameSize
;
629 // SMRAM range check already covered before
631 if (InfoSize
> CommBufferPayloadSize
) {
632 DEBUG ((EFI_D_ERROR
, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
633 Status
= EFI_ACCESS_DENIED
;
637 NameBufferSize
= CommBufferPayloadSize
- OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
);
638 if (NameBufferSize
< sizeof (CHAR16
) || GetNextVariableName
->Name
[NameBufferSize
/sizeof (CHAR16
) - 1] != L
'\0') {
640 // Make sure input VariableName is A Null-terminated string.
642 Status
= EFI_ACCESS_DENIED
;
646 Status
= VariableServiceGetNextVariableName (
647 &GetNextVariableName
->NameSize
,
648 GetNextVariableName
->Name
,
649 &GetNextVariableName
->Guid
651 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
654 case SMM_VARIABLE_FUNCTION_SET_VARIABLE
:
655 if (CommBufferPayloadSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) {
656 DEBUG ((EFI_D_ERROR
, "SetVariable: SMM communication buffer size invalid!\n"));
660 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
662 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
663 SmmVariableHeader
= (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*) mVariableBufferPayload
;
664 if (((UINTN
)(~0) - SmmVariableHeader
->DataSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)) ||
665 ((UINTN
)(~0) - SmmVariableHeader
->NameSize
< OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + SmmVariableHeader
->DataSize
)) {
667 // Prevent InfoSize overflow happen
669 Status
= EFI_ACCESS_DENIED
;
672 InfoSize
= OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
)
673 + SmmVariableHeader
->DataSize
+ SmmVariableHeader
->NameSize
;
676 // SMRAM range check already covered before
677 // Data buffer should not contain SMM range
679 if (InfoSize
> CommBufferPayloadSize
) {
680 DEBUG ((EFI_D_ERROR
, "SetVariable: Data size exceed communication buffer size limit!\n"));
681 Status
= EFI_ACCESS_DENIED
;
685 if (SmmVariableHeader
->NameSize
< sizeof (CHAR16
) || SmmVariableHeader
->Name
[SmmVariableHeader
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
687 // Make sure VariableName is A Null-terminated string.
689 Status
= EFI_ACCESS_DENIED
;
693 Status
= VariableServiceSetVariable (
694 SmmVariableHeader
->Name
,
695 &SmmVariableHeader
->Guid
,
696 SmmVariableHeader
->Attributes
,
697 SmmVariableHeader
->DataSize
,
698 (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
702 case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
:
703 if (CommBufferPayloadSize
< sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
)) {
704 DEBUG ((EFI_D_ERROR
, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
707 QueryVariableInfo
= (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*) SmmVariableFunctionHeader
->Data
;
709 Status
= VariableServiceQueryVariableInfo (
710 QueryVariableInfo
->Attributes
,
711 &QueryVariableInfo
->MaximumVariableStorageSize
,
712 &QueryVariableInfo
->RemainingVariableStorageSize
,
713 &QueryVariableInfo
->MaximumVariableSize
717 case SMM_VARIABLE_FUNCTION_READY_TO_BOOT
:
720 // The initialization for variable quota.
722 InitializeVariableQuota ();
724 Status
= EFI_UNSUPPORTED
;
728 Status
= EFI_SUCCESS
;
731 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
:
733 Status
= EFI_SUCCESS
;
736 case SMM_VARIABLE_FUNCTION_GET_STATISTICS
:
737 VariableInfo
= (VARIABLE_INFO_ENTRY
*) SmmVariableFunctionHeader
->Data
;
738 InfoSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
741 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
742 // It is covered by previous CommBuffer check
745 if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS
)(UINTN
)CommBufferSize
, sizeof(UINTN
))) {
746 DEBUG ((EFI_D_ERROR
, "GetStatistics: SMM communication buffer in SMRAM!\n"));
747 Status
= EFI_ACCESS_DENIED
;
751 Status
= SmmVariableGetStatistics (VariableInfo
, &InfoSize
);
752 *CommBufferSize
= InfoSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
755 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE
:
757 Status
= EFI_ACCESS_DENIED
;
759 VariableToLock
= (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*) SmmVariableFunctionHeader
->Data
;
760 Status
= VariableLockRequestToLock (
762 VariableToLock
->Name
,
763 &VariableToLock
->Guid
767 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET
:
769 Status
= EFI_ACCESS_DENIED
;
771 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) SmmVariableFunctionHeader
->Data
;
772 Status
= VarCheckVariablePropertySet (
773 CommVariableProperty
->Name
,
774 &CommVariableProperty
->Guid
,
775 &CommVariableProperty
->VariableProperty
779 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET
:
780 if (CommBufferPayloadSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
781 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
785 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
787 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
788 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) mVariableBufferPayload
;
789 if ((UINTN
) (~0) - CommVariableProperty
->NameSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
791 // Prevent InfoSize overflow happen
793 Status
= EFI_ACCESS_DENIED
;
796 InfoSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) + CommVariableProperty
->NameSize
;
799 // SMRAM range check already covered before
801 if (InfoSize
> CommBufferPayloadSize
) {
802 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
803 Status
= EFI_ACCESS_DENIED
;
807 if (CommVariableProperty
->NameSize
< sizeof (CHAR16
) || CommVariableProperty
->Name
[CommVariableProperty
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
809 // Make sure VariableName is A Null-terminated string.
811 Status
= EFI_ACCESS_DENIED
;
815 Status
= VarCheckVariablePropertyGet (
816 CommVariableProperty
->Name
,
817 &CommVariableProperty
->Guid
,
818 &CommVariableProperty
->VariableProperty
820 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
824 Status
= EFI_UNSUPPORTED
;
829 SmmVariableFunctionHeader
->ReturnStatus
= Status
;
834 SMM END_OF_DXE protocol notification event handler.
836 @param Protocol Points to the protocol's unique identifier
837 @param Interface Points to the interface instance
838 @param Handle The handle on which the interface was installed
840 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
845 SmmEndOfDxeCallback (
846 IN CONST EFI_GUID
*Protocol
,
851 DEBUG ((EFI_D_INFO
, "[Variable]END_OF_DXE is signaled\n"));
854 // The initialization for variable quota.
856 InitializeVariableQuota ();
857 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe
)) {
864 SMM Fault Tolerant Write protocol notification event handler.
866 Non-Volatile variable write may needs FTW protocol to reclaim when
869 @param Protocol Points to the protocol's unique identifier
870 @param Interface Points to the interface instance
871 @param Handle The handle on which the interface was installed
873 @retval EFI_SUCCESS SmmEventCallback runs successfully
874 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
879 SmmFtwNotificationEvent (
880 IN CONST EFI_GUID
*Protocol
,
886 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvbProtocol
;
887 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL
*FtwProtocol
;
888 EFI_PHYSICAL_ADDRESS NvStorageVariableBase
;
889 UINTN FtwMaxBlockSize
;
891 if (mVariableModuleGlobal
->FvbInstance
!= NULL
) {
896 // Ensure SMM FTW protocol is installed.
898 Status
= GetFtwProtocol ((VOID
**)&FtwProtocol
);
899 if (EFI_ERROR (Status
)) {
903 Status
= FtwProtocol
->GetMaxBlockSize (FtwProtocol
, &FtwMaxBlockSize
);
904 if (!EFI_ERROR (Status
)) {
905 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize
) <= FtwMaxBlockSize
);
909 // Find the proper FVB protocol for variable.
911 NvStorageVariableBase
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageVariableBase64
);
912 if (NvStorageVariableBase
== 0) {
913 NvStorageVariableBase
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageVariableBase
);
915 Status
= GetFvbInfoByAddress (NvStorageVariableBase
, NULL
, &FvbProtocol
);
916 if (EFI_ERROR (Status
)) {
917 return EFI_NOT_FOUND
;
920 mVariableModuleGlobal
->FvbInstance
= FvbProtocol
;
922 Status
= VariableWriteServiceInitialize ();
923 if (EFI_ERROR (Status
)) {
924 DEBUG ((DEBUG_ERROR
, "Variable write service initialization failed. Status = %r\n", Status
));
928 // Notify the variable wrapper driver the variable write service is ready
930 Status
= gBS
->InstallProtocolInterface (
932 &gSmmVariableWriteGuid
,
933 EFI_NATIVE_INTERFACE
,
936 ASSERT_EFI_ERROR (Status
);
943 Variable Driver main entry point. The Variable driver places the 4 EFI
944 runtime services in the EFI System Table and installs arch protocols
945 for variable read and write services being available. It also registers
946 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
948 @param[in] ImageHandle The firmware allocated handle for the EFI image.
949 @param[in] SystemTable A pointer to the EFI System Table.
951 @retval EFI_SUCCESS Variable service successfully initialized.
956 VariableServiceInitialize (
957 IN EFI_HANDLE ImageHandle
,
958 IN EFI_SYSTEM_TABLE
*SystemTable
962 EFI_HANDLE VariableHandle
;
963 VOID
*SmmFtwRegistration
;
964 EFI_SMM_ACCESS2_PROTOCOL
*SmmAccess
;
966 VOID
*SmmEndOfDxeRegistration
;
969 // Variable initialize.
971 Status
= VariableCommonInitialize ();
972 ASSERT_EFI_ERROR (Status
);
975 // Install the Smm Variable Protocol on a new handle.
977 VariableHandle
= NULL
;
978 Status
= gSmst
->SmmInstallProtocolInterface (
980 &gEfiSmmVariableProtocolGuid
,
981 EFI_NATIVE_INTERFACE
,
984 ASSERT_EFI_ERROR (Status
);
986 Status
= gSmst
->SmmInstallProtocolInterface (
988 &gEdkiiSmmVarCheckProtocolGuid
,
989 EFI_NATIVE_INTERFACE
,
992 ASSERT_EFI_ERROR (Status
);
995 // Get SMRAM information
997 Status
= gBS
->LocateProtocol (&gEfiSmmAccess2ProtocolGuid
, NULL
, (VOID
**)&SmmAccess
);
998 ASSERT_EFI_ERROR (Status
);
1001 Status
= SmmAccess
->GetCapabilities (SmmAccess
, &Size
, NULL
);
1002 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
1004 Status
= gSmst
->SmmAllocatePool (
1005 EfiRuntimeServicesData
,
1007 (VOID
**)&mSmramRanges
1009 ASSERT_EFI_ERROR (Status
);
1011 Status
= SmmAccess
->GetCapabilities (SmmAccess
, &Size
, mSmramRanges
);
1012 ASSERT_EFI_ERROR (Status
);
1014 mSmramRangeCount
= Size
/ sizeof (EFI_SMRAM_DESCRIPTOR
);
1016 mVariableBufferPayloadSize
= MAX (PcdGet32 (PcdMaxVariableSize
), PcdGet32 (PcdMaxHardwareErrorVariableSize
)) +
1017 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) - sizeof (VARIABLE_HEADER
);
1019 Status
= gSmst
->SmmAllocatePool (
1020 EfiRuntimeServicesData
,
1021 mVariableBufferPayloadSize
,
1022 (VOID
**)&mVariableBufferPayload
1024 ASSERT_EFI_ERROR (Status
);
1027 /// Register SMM variable SMI handler
1029 VariableHandle
= NULL
;
1030 Status
= gSmst
->SmiHandlerRegister (SmmVariableHandler
, &gEfiSmmVariableProtocolGuid
, &VariableHandle
);
1031 ASSERT_EFI_ERROR (Status
);
1034 // Notify the variable wrapper driver the variable service is ready
1036 Status
= SystemTable
->BootServices
->InstallProtocolInterface (
1038 &gEfiSmmVariableProtocolGuid
,
1039 EFI_NATIVE_INTERFACE
,
1042 ASSERT_EFI_ERROR (Status
);
1045 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
1047 Status
= gSmst
->SmmRegisterProtocolNotify (
1048 &gEfiSmmEndOfDxeProtocolGuid
,
1049 SmmEndOfDxeCallback
,
1050 &SmmEndOfDxeRegistration
1052 ASSERT_EFI_ERROR (Status
);
1055 // Register FtwNotificationEvent () notify function.
1057 Status
= gSmst
->SmmRegisterProtocolNotify (
1058 &gEfiSmmFaultTolerantWriteProtocolGuid
,
1059 SmmFtwNotificationEvent
,
1062 ASSERT_EFI_ERROR (Status
);
1064 SmmFtwNotificationEvent (NULL
, NULL
, NULL
);