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 - 2016, Intel Corporation. All rights reserved.<BR>
18 Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
19 This program and the accompanying materials
20 are licensed and made available under the terms and conditions of the BSD License
21 which accompanies this distribution. The full text of the license may be found at
22 http://opensource.org/licenses/bsd-license.php
24 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
25 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
29 #include <Protocol/SmmVariable.h>
30 #include <Protocol/SmmFirmwareVolumeBlock.h>
31 #include <Protocol/SmmFaultTolerantWrite.h>
32 #include <Protocol/MmEndOfDxe.h>
33 #include <Protocol/SmmVarCheck.h>
35 #include <Library/MmServicesTableLib.h>
37 #include <Guid/SmmVariableCommon.h>
40 extern VARIABLE_INFO_ENTRY
*gVariableInfo
;
41 BOOLEAN mAtRuntime
= FALSE
;
42 UINT8
*mVariableBufferPayload
= NULL
;
43 UINTN mVariableBufferPayloadSize
;
44 extern BOOLEAN mEndOfDxe
;
45 extern VAR_CHECK_REQUEST_SOURCE mRequestSource
;
48 SecureBoot Hook for SetVariable.
50 @param[in] VariableName Name of Variable to be found.
51 @param[in] VendorGuid Variable vendor GUID.
57 IN CHAR16
*VariableName
,
58 IN EFI_GUID
*VendorGuid
66 This code sets variable in storage blocks (Volatile or Non-Volatile).
68 @param VariableName Name of Variable to be found.
69 @param VendorGuid Variable vendor GUID.
70 @param Attributes Attribute value of the variable found
71 @param DataSize Size of Data found. If size is less than the
72 data, this value contains the required size.
73 @param Data Data pointer.
75 @return EFI_INVALID_PARAMETER Invalid parameter.
76 @return EFI_SUCCESS Set successfully.
77 @return EFI_OUT_OF_RESOURCES Resource not enough to set variable.
78 @return EFI_NOT_FOUND Not found.
79 @return EFI_WRITE_PROTECTED Variable is read-only.
84 SmmVariableSetVariable (
85 IN CHAR16
*VariableName
,
86 IN EFI_GUID
*VendorGuid
,
95 // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
97 mRequestSource
= VarCheckFromTrusted
;
98 Status
= VariableServiceSetVariable (
105 mRequestSource
= VarCheckFromUntrusted
;
109 EFI_SMM_VARIABLE_PROTOCOL gSmmVariable
= {
110 VariableServiceGetVariable
,
111 VariableServiceGetNextVariableName
,
112 SmmVariableSetVariable
,
113 VariableServiceQueryVariableInfo
116 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck
= { VarCheckRegisterSetVariableCheckHandler
,
117 VarCheckVariablePropertySet
,
118 VarCheckVariablePropertyGet
};
121 Return TRUE if ExitBootServices () has been called.
123 @retval TRUE If ExitBootServices () has been called.
134 Initializes a basic mutual exclusion lock.
136 This function initializes a basic mutual exclusion lock to the released state
137 and returns the lock. Each lock provides mutual exclusion access at its task
138 priority level. Since there is no preemption or multiprocessor support in EFI,
139 acquiring the lock only consists of raising to the locks TPL.
140 If Lock is NULL, then ASSERT().
141 If Priority is not a valid TPL value, then ASSERT().
143 @param Lock A pointer to the lock data structure to initialize.
144 @param Priority EFI TPL is associated with the lock.
151 IN OUT EFI_LOCK
*Lock
,
159 Acquires lock only at boot time. Simply returns at runtime.
161 This is a temperary function that will be removed when
162 EfiAcquireLock() in UefiLib can handle the call in UEFI
163 Runtimer driver in RT phase.
164 It calls EfiAcquireLock() at boot time, and simply returns
167 @param Lock A pointer to the lock to acquire.
171 AcquireLockOnlyAtBootTime (
180 Releases lock only at boot time. Simply returns at runtime.
182 This is a temperary function which will be removed when
183 EfiReleaseLock() in UefiLib can handle the call in UEFI
184 Runtimer driver in RT phase.
185 It calls EfiReleaseLock() at boot time and simply returns
188 @param Lock A pointer to the lock to release.
192 ReleaseLockOnlyAtBootTime (
200 Retrieve the SMM Fault Tolerent Write protocol interface.
202 @param[out] FtwProtocol The interface of SMM Ftw protocol
204 @retval EFI_SUCCESS The SMM FTW protocol instance was found and returned in FtwProtocol.
205 @retval EFI_NOT_FOUND The SMM FTW protocol instance was not found.
206 @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
211 OUT VOID
**FtwProtocol
217 // Locate Smm Fault Tolerent Write protocol
219 Status
= gMmst
->MmLocateProtocol (
220 &gEfiSmmFaultTolerantWriteProtocolGuid
,
229 Retrieve the SMM FVB protocol interface by HANDLE.
231 @param[in] FvBlockHandle The handle of SMM FVB protocol that provides services for
232 reading, writing, and erasing the target block.
233 @param[out] FvBlock The interface of SMM FVB protocol
235 @retval EFI_SUCCESS The interface information for the specified protocol was returned.
236 @retval EFI_UNSUPPORTED The device does not support the SMM FVB protocol.
237 @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
242 IN EFI_HANDLE FvBlockHandle
,
243 OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL
**FvBlock
247 // To get the SMM FVB protocol interface on the handle
249 return gMmst
->MmHandleProtocol (
251 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
258 Function returns an array of handles that support the SMM FVB protocol
259 in a buffer allocated from pool.
261 @param[out] NumberHandles The number of handles returned in Buffer.
262 @param[out] Buffer A pointer to the buffer to return the requested
263 array of handles that support SMM FVB protocol.
265 @retval EFI_SUCCESS The array of handles was returned in Buffer, and the number of
266 handles in Buffer was returned in NumberHandles.
267 @retval EFI_NOT_FOUND No SMM FVB handle was found.
268 @retval EFI_OUT_OF_RESOURCES There is not enough pool memory to store the matching results.
269 @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
273 GetFvbCountAndBuffer (
274 OUT UINTN
*NumberHandles
,
275 OUT EFI_HANDLE
**Buffer
281 if ((NumberHandles
== NULL
) || (Buffer
== NULL
)) {
282 return EFI_INVALID_PARAMETER
;
288 Status
= gMmst
->MmLocateHandle (
290 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
295 if (EFI_ERROR(Status
) && Status
!= EFI_BUFFER_TOO_SMALL
) {
296 return EFI_NOT_FOUND
;
299 *Buffer
= AllocatePool (BufferSize
);
300 if (*Buffer
== NULL
) {
301 return EFI_OUT_OF_RESOURCES
;
304 Status
= gMmst
->MmLocateHandle (
306 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
312 *NumberHandles
= BufferSize
/ sizeof(EFI_HANDLE
);
313 if (EFI_ERROR(Status
)) {
324 Get the variable statistics information from the information buffer pointed by gVariableInfo.
326 Caution: This function may be invoked at SMM runtime.
327 InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
329 @param[in, out] InfoEntry A pointer to the buffer of variable information entry.
330 On input, point to the variable information returned last time. if
331 InfoEntry->VendorGuid is zero, return the first information.
332 On output, point to the next variable information.
333 @param[in, out] InfoSize On input, the size of the variable information buffer.
334 On output, the returned variable information size.
336 @retval EFI_SUCCESS The variable information is found and returned successfully.
337 @retval EFI_UNSUPPORTED No variable inoformation exists in variable driver. The
338 PcdVariableCollectStatistics should be set TRUE to support it.
339 @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
340 @retval EFI_INVALID_PARAMETER Input parameter is invalid.
344 SmmVariableGetStatistics (
345 IN OUT VARIABLE_INFO_ENTRY
*InfoEntry
,
346 IN OUT UINTN
*InfoSize
349 VARIABLE_INFO_ENTRY
*VariableInfo
;
351 UINTN StatisticsInfoSize
;
353 UINTN InfoNameMaxSize
;
356 if (InfoEntry
== NULL
) {
357 return EFI_INVALID_PARAMETER
;
360 VariableInfo
= gVariableInfo
;
361 if (VariableInfo
== NULL
) {
362 return EFI_UNSUPPORTED
;
365 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
);
366 if (*InfoSize
< StatisticsInfoSize
) {
367 *InfoSize
= StatisticsInfoSize
;
368 return EFI_BUFFER_TOO_SMALL
;
370 InfoName
= (CHAR16
*)(InfoEntry
+ 1);
371 InfoNameMaxSize
= (*InfoSize
- sizeof (VARIABLE_INFO_ENTRY
));
373 CopyGuid (&VendorGuid
, &InfoEntry
->VendorGuid
);
375 if (IsZeroGuid (&VendorGuid
)) {
377 // Return the first variable info
379 NameSize
= StrSize (VariableInfo
->Name
);
380 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
381 if (*InfoSize
< StatisticsInfoSize
) {
382 *InfoSize
= StatisticsInfoSize
;
383 return EFI_BUFFER_TOO_SMALL
;
385 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
386 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
387 *InfoSize
= StatisticsInfoSize
;
392 // Get the next variable info
394 while (VariableInfo
!= NULL
) {
395 if (CompareGuid (&VariableInfo
->VendorGuid
, &VendorGuid
)) {
396 NameSize
= StrSize (VariableInfo
->Name
);
397 if (NameSize
<= InfoNameMaxSize
) {
398 if (CompareMem (VariableInfo
->Name
, InfoName
, NameSize
) == 0) {
400 // Find the match one
402 VariableInfo
= VariableInfo
->Next
;
407 VariableInfo
= VariableInfo
->Next
;
410 if (VariableInfo
== NULL
) {
416 // Output the new variable info
418 NameSize
= StrSize (VariableInfo
->Name
);
419 StatisticsInfoSize
= sizeof (VARIABLE_INFO_ENTRY
) + NameSize
;
420 if (*InfoSize
< StatisticsInfoSize
) {
421 *InfoSize
= StatisticsInfoSize
;
422 return EFI_BUFFER_TOO_SMALL
;
425 CopyMem (InfoEntry
, VariableInfo
, sizeof (VARIABLE_INFO_ENTRY
));
426 CopyMem (InfoName
, VariableInfo
->Name
, NameSize
);
427 *InfoSize
= StatisticsInfoSize
;
434 Communication service SMI Handler entry.
436 This SMI handler provides services for the variable wrapper driver.
438 Caution: This function may receive untrusted input.
439 This variable data and communicate buffer are external input, so this function will do basic validation.
440 Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
441 VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
442 SmmVariableGetStatistics() should also do validation based on its own knowledge.
444 @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
445 @param[in] RegisterContext Points to an optional handler context which was specified when the
446 handler was registered.
447 @param[in, out] CommBuffer A pointer to a collection of data in memory that will
448 be conveyed from a non-SMM environment into an SMM environment.
449 @param[in, out] CommBufferSize The size of the CommBuffer.
451 @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
452 should still be called.
453 @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
455 @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
457 @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
462 IN EFI_HANDLE DispatchHandle
,
463 IN CONST VOID
*RegisterContext
,
464 IN OUT VOID
*CommBuffer
,
465 IN OUT UINTN
*CommBufferSize
469 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
470 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
471 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*GetNextVariableName
;
472 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*QueryVariableInfo
;
473 SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE
*GetPayloadSize
;
474 VARIABLE_INFO_ENTRY
*VariableInfo
;
475 SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*VariableToLock
;
476 SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*CommVariableProperty
;
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 ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
495 CommBufferPayloadSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
496 if (CommBufferPayloadSize
> mVariableBufferPayloadSize
) {
497 DEBUG ((EFI_D_ERROR
, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
501 if (!VariableSmmIsBufferOutsideSmmValid ((UINTN
)CommBuffer
, TempCommBufferSize
)) {
502 DEBUG ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ((EFI_D_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 ();
694 VarCheckLibInitializeAtEndOfDxe (NULL
);
696 // The initialization for variable quota.
698 InitializeVariableQuota ();
701 Status
= EFI_SUCCESS
;
704 case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
:
706 Status
= EFI_SUCCESS
;
709 case SMM_VARIABLE_FUNCTION_GET_STATISTICS
:
710 VariableInfo
= (VARIABLE_INFO_ENTRY
*) SmmVariableFunctionHeader
->Data
;
711 InfoSize
= TempCommBufferSize
- SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
714 // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
715 // It is covered by previous CommBuffer check
719 // Do not need to check CommBufferSize buffer as it should point to SMRAM
720 // that was used by SMM core to cache CommSize from SmmCommunication protocol.
723 Status
= SmmVariableGetStatistics (VariableInfo
, &InfoSize
);
724 *CommBufferSize
= InfoSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
727 case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE
:
729 Status
= EFI_ACCESS_DENIED
;
731 VariableToLock
= (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE
*) SmmVariableFunctionHeader
->Data
;
732 Status
= VariableLockRequestToLock (
734 VariableToLock
->Name
,
735 &VariableToLock
->Guid
739 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET
:
741 Status
= EFI_ACCESS_DENIED
;
743 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) SmmVariableFunctionHeader
->Data
;
744 Status
= VarCheckVariablePropertySet (
745 CommVariableProperty
->Name
,
746 &CommVariableProperty
->Guid
,
747 &CommVariableProperty
->VariableProperty
751 case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET
:
752 if (CommBufferPayloadSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
753 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
757 // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
759 CopyMem (mVariableBufferPayload
, SmmVariableFunctionHeader
->Data
, CommBufferPayloadSize
);
760 CommVariableProperty
= (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
*) mVariableBufferPayload
;
761 if ((UINTN
) (~0) - CommVariableProperty
->NameSize
< OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
)) {
763 // Prevent InfoSize overflow happen
765 Status
= EFI_ACCESS_DENIED
;
768 InfoSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) + CommVariableProperty
->NameSize
;
771 // SMRAM range check already covered before
773 if (InfoSize
> CommBufferPayloadSize
) {
774 DEBUG ((EFI_D_ERROR
, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
775 Status
= EFI_ACCESS_DENIED
;
780 // The VariableSpeculationBarrier() call here is to ensure the previous
781 // range/content checks for the CommBuffer have been completed before the
782 // subsequent consumption of the CommBuffer content.
784 VariableSpeculationBarrier ();
785 if (CommVariableProperty
->NameSize
< sizeof (CHAR16
) || CommVariableProperty
->Name
[CommVariableProperty
->NameSize
/sizeof (CHAR16
) - 1] != L
'\0') {
787 // Make sure VariableName is A Null-terminated string.
789 Status
= EFI_ACCESS_DENIED
;
793 Status
= VarCheckVariablePropertyGet (
794 CommVariableProperty
->Name
,
795 &CommVariableProperty
->Guid
,
796 &CommVariableProperty
->VariableProperty
798 CopyMem (SmmVariableFunctionHeader
->Data
, mVariableBufferPayload
, CommBufferPayloadSize
);
802 Status
= EFI_UNSUPPORTED
;
807 SmmVariableFunctionHeader
->ReturnStatus
= Status
;
813 SMM END_OF_DXE protocol notification event handler.
815 @param Protocol Points to the protocol's unique identifier
816 @param Interface Points to the interface instance
817 @param Handle The handle on which the interface was installed
819 @retval EFI_SUCCESS SmmEndOfDxeCallback runs successfully
824 SmmEndOfDxeCallback (
825 IN CONST EFI_GUID
*Protocol
,
830 DEBUG ((EFI_D_INFO
, "[Variable]SMM_END_OF_DXE is signaled\n"));
831 MorLockInitAtEndOfDxe ();
833 VarCheckLibInitializeAtEndOfDxe (NULL
);
835 // The initialization for variable quota.
837 InitializeVariableQuota ();
838 if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe
)) {
846 SMM Fault Tolerant Write protocol notification event handler.
848 Non-Volatile variable write may needs FTW protocol to reclaim when
851 @param Protocol Points to the protocol's unique identifier
852 @param Interface Points to the interface instance
853 @param Handle The handle on which the interface was installed
855 @retval EFI_SUCCESS SmmEventCallback runs successfully
856 @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
861 SmmFtwNotificationEvent (
862 IN CONST EFI_GUID
*Protocol
,
868 EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL
*FvbProtocol
;
869 EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL
*FtwProtocol
;
870 EFI_PHYSICAL_ADDRESS NvStorageVariableBase
;
871 UINTN FtwMaxBlockSize
;
873 if (mVariableModuleGlobal
->FvbInstance
!= NULL
) {
878 // Ensure SMM FTW protocol is installed.
880 Status
= GetFtwProtocol ((VOID
**)&FtwProtocol
);
881 if (EFI_ERROR (Status
)) {
885 Status
= FtwProtocol
->GetMaxBlockSize (FtwProtocol
, &FtwMaxBlockSize
);
886 if (!EFI_ERROR (Status
)) {
887 ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize
) <= FtwMaxBlockSize
);
891 // Find the proper FVB protocol for variable.
893 NvStorageVariableBase
= (EFI_PHYSICAL_ADDRESS
) PcdGet64 (PcdFlashNvStorageVariableBase64
);
894 if (NvStorageVariableBase
== 0) {
895 NvStorageVariableBase
= (EFI_PHYSICAL_ADDRESS
) PcdGet32 (PcdFlashNvStorageVariableBase
);
897 Status
= GetFvbInfoByAddress (NvStorageVariableBase
, NULL
, &FvbProtocol
);
898 if (EFI_ERROR (Status
)) {
899 return EFI_NOT_FOUND
;
902 mVariableModuleGlobal
->FvbInstance
= FvbProtocol
;
904 Status
= VariableWriteServiceInitialize ();
905 if (EFI_ERROR (Status
)) {
906 DEBUG ((DEBUG_ERROR
, "Variable write service initialization failed. Status = %r\n", Status
));
910 // Notify the variable wrapper driver the variable write service is ready
912 VariableNotifySmmWriteReady ();
919 Variable Driver main entry point. The Variable driver places the 4 EFI
920 runtime services in the EFI System Table and installs arch protocols
921 for variable read and write services being available. It also registers
922 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
924 @retval EFI_SUCCESS Variable service successfully initialized.
929 MmVariableServiceInitialize (
934 EFI_HANDLE VariableHandle
;
935 VOID
*SmmFtwRegistration
;
936 VOID
*SmmEndOfDxeRegistration
;
939 // Variable initialize.
941 Status
= VariableCommonInitialize ();
942 ASSERT_EFI_ERROR (Status
);
945 // Install the Smm Variable Protocol on a new handle.
947 VariableHandle
= NULL
;
948 Status
= gMmst
->MmInstallProtocolInterface (
950 &gEfiSmmVariableProtocolGuid
,
951 EFI_NATIVE_INTERFACE
,
954 ASSERT_EFI_ERROR (Status
);
956 Status
= gMmst
->MmInstallProtocolInterface (
958 &gEdkiiSmmVarCheckProtocolGuid
,
959 EFI_NATIVE_INTERFACE
,
962 ASSERT_EFI_ERROR (Status
);
964 mVariableBufferPayloadSize
= GetMaxVariableSize () +
965 OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY
, Name
) - GetVariableHeaderSize ();
967 Status
= gMmst
->MmAllocatePool (
968 EfiRuntimeServicesData
,
969 mVariableBufferPayloadSize
,
970 (VOID
**)&mVariableBufferPayload
972 ASSERT_EFI_ERROR (Status
);
975 /// Register SMM variable SMI handler
977 VariableHandle
= NULL
;
978 Status
= gMmst
->MmiHandlerRegister (SmmVariableHandler
, &gEfiSmmVariableProtocolGuid
, &VariableHandle
);
979 ASSERT_EFI_ERROR (Status
);
982 // Notify the variable wrapper driver the variable service is ready
984 VariableNotifySmmReady ();
987 // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
989 Status
= gMmst
->MmRegisterProtocolNotify (
990 &gEfiMmEndOfDxeProtocolGuid
,
992 &SmmEndOfDxeRegistration
994 ASSERT_EFI_ERROR (Status
);
997 // Register FtwNotificationEvent () notify function.
999 Status
= gMmst
->MmRegisterProtocolNotify (
1000 &gEfiSmmFaultTolerantWriteProtocolGuid
,
1001 SmmFtwNotificationEvent
,
1004 ASSERT_EFI_ERROR (Status
);
1006 SmmFtwNotificationEvent (NULL
, NULL
, NULL
);