3 Implement all four UEFI Runtime Variable services for the nonvolatile
4 and volatile storage space and install variable architecture protocol
5 based on SMM variable module.
7 Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include <Protocol/VariableWrite.h>
19 #include <Protocol/Variable.h>
20 #include <Protocol/SmmCommunication.h>
21 #include <Protocol/SmmVariable.h>
23 #include <Library/UefiBootServicesTableLib.h>
24 #include <Library/UefiRuntimeServicesTableLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/UefiDriverEntryPoint.h>
27 #include <Library/UefiRuntimeLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/PcdLib.h>
31 #include <Library/UefiLib.h>
32 #include <Library/BaseLib.h>
34 #include <Guid/EventGroup.h>
35 #include <Guid/VariableFormat.h>
36 #include <Guid/SmmVariableCommon.h>
38 EFI_HANDLE mHandle
= NULL
;
39 EFI_SMM_VARIABLE_PROTOCOL
*mSmmVariable
= NULL
;
40 EFI_EVENT mVirtualAddressChangeEvent
= NULL
;
41 EFI_SMM_COMMUNICATION_PROTOCOL
*mSmmCommunication
= NULL
;
42 UINT8
*mVariableBuffer
= NULL
;
43 UINT8
*mVariableBufferPhysical
= NULL
;
44 UINTN mVariableBufferSize
;
45 EFI_LOCK mVariableServicesLock
;
48 Acquires lock only at boot time. Simply returns at runtime.
50 This is a temperary function that will be removed when
51 EfiAcquireLock() in UefiLib can handle the call in UEFI
52 Runtimer driver in RT phase.
53 It calls EfiAcquireLock() at boot time, and simply returns
56 @param Lock A pointer to the lock to acquire.
60 AcquireLockOnlyAtBootTime (
64 if (!EfiAtRuntime ()) {
65 EfiAcquireLock (Lock
);
70 Releases lock only at boot time. Simply returns at runtime.
72 This is a temperary function which will be removed when
73 EfiReleaseLock() in UefiLib can handle the call in UEFI
74 Runtimer driver in RT phase.
75 It calls EfiReleaseLock() at boot time and simply returns
78 @param Lock A pointer to the lock to release.
82 ReleaseLockOnlyAtBootTime (
86 if (!EfiAtRuntime ()) {
87 EfiReleaseLock (Lock
);
92 Initialize the communicate buffer using DataSize and Function.
94 The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
97 @param[out] DataPtr Points to the data in the communicate buffer.
98 @param[in] DataSize The data size to send to SMM.
99 @param[in] Function The function number to initialize the communicate header.
101 @retval EFI_INVALID_PARAMETER The data size is too big.
102 @retval EFI_SUCCESS Find the specified variable.
106 InitCommunicateBuffer (
107 OUT VOID
**DataPtr OPTIONAL
,
112 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
113 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
116 if (DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
> mVariableBufferSize
) {
117 return EFI_INVALID_PARAMETER
;
120 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
121 CopyGuid (&SmmCommunicateHeader
->HeaderGuid
, &gEfiSmmVariableProtocolGuid
);
122 SmmCommunicateHeader
->MessageLength
= DataSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
124 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*) SmmCommunicateHeader
->Data
;
125 SmmVariableFunctionHeader
->Function
= Function
;
126 if (DataPtr
!= NULL
) {
127 *DataPtr
= SmmVariableFunctionHeader
->Data
;
135 Send the data in communicate buffer to SMM.
137 @param[in] DataSize This size of the function header and the data.
139 @retval EFI_SUCCESS Success is returned from the functin in SMM.
140 @retval Others Failure is returned from the function in SMM.
144 SendCommunicateBuffer (
150 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
151 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
153 CommSize
= DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
154 Status
= mSmmCommunication
->Communicate (mSmmCommunication
, mVariableBufferPhysical
, &CommSize
);
155 ASSERT_EFI_ERROR (Status
);
157 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
158 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)SmmCommunicateHeader
->Data
;
159 return SmmVariableFunctionHeader
->ReturnStatus
;
164 This code finds variable in storage blocks (Volatile or Non-Volatile).
166 @param[in] VariableName Name of Variable to be found.
167 @param[in] VendorGuid Variable vendor GUID.
168 @param[out] Attributes Attribute value of the variable found.
169 @param[in, out] DataSize Size of Data found. If size is less than the
170 data, this value contains the required size.
171 @param[out] Data Data pointer.
173 @retval EFI_INVALID_PARAMETER Invalid parameter.
174 @retval EFI_SUCCESS Find the specified variable.
175 @retval EFI_NOT_FOUND Not found.
176 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
181 RuntimeServiceGetVariable (
182 IN CHAR16
*VariableName
,
183 IN EFI_GUID
*VendorGuid
,
184 OUT UINT32
*Attributes OPTIONAL
,
185 IN OUT UINTN
*DataSize
,
191 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
193 if (VariableName
== NULL
|| VendorGuid
== NULL
|| DataSize
== NULL
) {
194 return EFI_INVALID_PARAMETER
;
197 if ((*DataSize
!= 0) && (Data
== NULL
)) {
198 return EFI_INVALID_PARAMETER
;
201 if (*DataSize
>= mVariableBufferSize
) {
203 // DataSize may be near MAX_ADDRESS incorrectly, this can cause the computed PayLoadSize to
204 // overflow to a small value and pass the check in InitCommunicateBuffer().
205 // To protect against this vulnerability, return EFI_INVALID_PARAMETER if DataSize is >= mVariableBufferSize.
206 // And there will be further check to ensure the total size is also not > mVariableBufferSize.
208 return EFI_INVALID_PARAMETER
;
211 AcquireLockOnlyAtBootTime(&mVariableServicesLock
);
214 // Init the communicate buffer. The buffer data size is:
215 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
217 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
) + *DataSize
;
218 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_VARIABLE
);
219 if (EFI_ERROR (Status
)) {
222 ASSERT (SmmVariableHeader
!= NULL
);
224 CopyGuid (&SmmVariableHeader
->Guid
, VendorGuid
);
225 SmmVariableHeader
->DataSize
= *DataSize
;
226 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
227 if (Attributes
== NULL
) {
228 SmmVariableHeader
->Attributes
= 0;
230 SmmVariableHeader
->Attributes
= *Attributes
;
232 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
237 Status
= SendCommunicateBuffer (PayloadSize
);
240 // Get data from SMM.
242 *DataSize
= SmmVariableHeader
->DataSize
;
243 if (Attributes
!= NULL
) {
244 *Attributes
= SmmVariableHeader
->Attributes
;
247 if (EFI_ERROR (Status
)) {
251 CopyMem (Data
, (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, SmmVariableHeader
->DataSize
);
254 ReleaseLockOnlyAtBootTime (&mVariableServicesLock
);
260 This code Finds the Next available variable.
262 @param[in, out] VariableNameSize Size of the variable name.
263 @param[in, out] VariableName Pointer to variable name.
264 @param[in, out] VendorGuid Variable Vendor Guid.
266 @retval EFI_INVALID_PARAMETER Invalid parameter.
267 @retval EFI_SUCCESS Find the specified variable.
268 @retval EFI_NOT_FOUND Not found.
269 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
274 RuntimeServiceGetNextVariableName (
275 IN OUT UINTN
*VariableNameSize
,
276 IN OUT CHAR16
*VariableName
,
277 IN OUT EFI_GUID
*VendorGuid
282 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*SmmGetNextVariableName
;
284 if (VariableNameSize
== NULL
|| VariableName
== NULL
|| VendorGuid
== NULL
) {
285 return EFI_INVALID_PARAMETER
;
288 if (*VariableNameSize
>= mVariableBufferSize
) {
290 // VariableNameSize may be near MAX_ADDRESS incorrectly, this can cause the computed PayLoadSize to
291 // overflow to a small value and pass the check in InitCommunicateBuffer().
292 // To protect against this vulnerability, return EFI_INVALID_PARAMETER if VariableNameSize is >= mVariableBufferSize.
293 // And there will be further check to ensure the total size is also not > mVariableBufferSize.
295 return EFI_INVALID_PARAMETER
;
298 AcquireLockOnlyAtBootTime(&mVariableServicesLock
);
301 // Init the communicate buffer. The buffer data size is:
302 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
304 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + *VariableNameSize
;
305 Status
= InitCommunicateBuffer ((VOID
**)&SmmGetNextVariableName
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
);
306 if (EFI_ERROR (Status
)) {
309 ASSERT (SmmGetNextVariableName
!= NULL
);
311 SmmGetNextVariableName
->NameSize
= *VariableNameSize
;
312 CopyGuid (&SmmGetNextVariableName
->Guid
, VendorGuid
);
313 CopyMem (SmmGetNextVariableName
->Name
, VariableName
, *VariableNameSize
);
318 Status
= SendCommunicateBuffer (PayloadSize
);
321 // Get data from SMM.
323 *VariableNameSize
= SmmGetNextVariableName
->NameSize
;
324 if (EFI_ERROR (Status
)) {
328 CopyGuid (VendorGuid
, &SmmGetNextVariableName
->Guid
);
329 CopyMem (VariableName
, SmmGetNextVariableName
->Name
, SmmGetNextVariableName
->NameSize
);
332 ReleaseLockOnlyAtBootTime (&mVariableServicesLock
);
337 This code sets variable in storage blocks (Volatile or Non-Volatile).
339 @param[in] VariableName Name of Variable to be found.
340 @param[in] VendorGuid Variable vendor GUID.
341 @param[in] Attributes Attribute value of the variable found
342 @param[in] DataSize Size of Data found. If size is less than the
343 data, this value contains the required size.
344 @param[in] Data Data pointer.
346 @retval EFI_INVALID_PARAMETER Invalid parameter.
347 @retval EFI_SUCCESS Set successfully.
348 @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
349 @retval EFI_NOT_FOUND Not found.
350 @retval EFI_WRITE_PROTECTED Variable is read-only.
355 RuntimeServiceSetVariable (
356 IN CHAR16
*VariableName
,
357 IN EFI_GUID
*VendorGuid
,
358 IN UINT32 Attributes
,
365 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
368 // Check input parameters.
370 if (VariableName
== NULL
|| VariableName
[0] == 0 || VendorGuid
== NULL
) {
371 return EFI_INVALID_PARAMETER
;
374 if (DataSize
!= 0 && Data
== NULL
) {
375 return EFI_INVALID_PARAMETER
;
378 if (DataSize
>= mVariableBufferSize
) {
380 // DataSize may be near MAX_ADDRESS incorrectly, this can cause the computed PayLoadSize to
381 // overflow to a small value and pass the check in InitCommunicateBuffer().
382 // To protect against this vulnerability, return EFI_INVALID_PARAMETER if DataSize is >= mVariableBufferSize.
383 // And there will be further check to ensure the total size is also not > mVariableBufferSize.
385 return EFI_INVALID_PARAMETER
;
388 AcquireLockOnlyAtBootTime(&mVariableServicesLock
);
391 // Init the communicate buffer. The buffer data size is:
392 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
394 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
) + DataSize
;
395 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_SET_VARIABLE
);
396 if (EFI_ERROR (Status
)) {
399 ASSERT (SmmVariableHeader
!= NULL
);
401 CopyGuid ((EFI_GUID
*) &SmmVariableHeader
->Guid
, VendorGuid
);
402 SmmVariableHeader
->DataSize
= DataSize
;
403 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
404 SmmVariableHeader
->Attributes
= Attributes
;
405 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
406 CopyMem ((UINT8
*) SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, Data
, DataSize
);
411 Status
= SendCommunicateBuffer (PayloadSize
);
414 ReleaseLockOnlyAtBootTime (&mVariableServicesLock
);
420 This code returns information about the EFI variables.
422 @param[in] Attributes Attributes bitmask to specify the type of variables
423 on which to return information.
424 @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available
425 for the EFI variables associated with the attributes specified.
426 @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available
427 for EFI variables associated with the attributes specified.
428 @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables
429 associated with the attributes specified.
431 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.
432 @retval EFI_SUCCESS Query successfully.
433 @retval EFI_UNSUPPORTED The attribute is not supported on this platform.
438 RuntimeServiceQueryVariableInfo (
439 IN UINT32 Attributes
,
440 OUT UINT64
*MaximumVariableStorageSize
,
441 OUT UINT64
*RemainingVariableStorageSize
,
442 OUT UINT64
*MaximumVariableSize
447 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*SmmQueryVariableInfo
;
449 if(MaximumVariableStorageSize
== NULL
|| RemainingVariableStorageSize
== NULL
|| MaximumVariableSize
== NULL
|| Attributes
== 0) {
450 return EFI_INVALID_PARAMETER
;
453 AcquireLockOnlyAtBootTime(&mVariableServicesLock
);
456 // Init the communicate buffer. The buffer data size is:
457 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize;
459 PayloadSize
= sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
);
460 Status
= InitCommunicateBuffer ((VOID
**)&SmmQueryVariableInfo
, PayloadSize
, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
);
461 if (EFI_ERROR (Status
)) {
464 ASSERT (SmmQueryVariableInfo
!= NULL
);
466 SmmQueryVariableInfo
->Attributes
= Attributes
;
471 Status
= SendCommunicateBuffer (PayloadSize
);
472 if (EFI_ERROR (Status
)) {
477 // Get data from SMM.
479 *MaximumVariableSize
= SmmQueryVariableInfo
->MaximumVariableSize
;
480 *MaximumVariableStorageSize
= SmmQueryVariableInfo
->MaximumVariableStorageSize
;
481 *RemainingVariableStorageSize
= SmmQueryVariableInfo
->RemainingVariableStorageSize
;
484 ReleaseLockOnlyAtBootTime (&mVariableServicesLock
);
490 Exit Boot Services Event notification handler.
492 Notify SMM variable driver about the event.
494 @param[in] Event Event whose notification function is being invoked.
495 @param[in] Context Pointer to the notification function's context.
506 // Init the communicate buffer. The buffer data size is:
507 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
509 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
);
514 SendCommunicateBuffer (0);
519 On Ready To Boot Services Event notification handler.
521 Notify SMM variable driver about the event.
523 @param[in] Event Event whose notification function is being invoked
524 @param[in] Context Pointer to the notification function's context
535 // Init the communicate buffer. The buffer data size is:
536 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
538 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT
);
543 SendCommunicateBuffer (0);
548 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
550 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
551 It convers pointer to new virtual address.
553 @param[in] Event Event whose notification function is being invoked.
554 @param[in] Context Pointer to the notification function's context.
559 VariableAddressChangeEvent (
564 EfiConvertPointer (0x0, (VOID
**) &mVariableBuffer
);
565 EfiConvertPointer (0x0, (VOID
**) &mSmmCommunication
);
570 Initialize variable service and install Variable Architectural protocol.
572 @param[in] Event Event whose notification function is being invoked.
573 @param[in] Context Pointer to the notification function's context.
585 Status
= gBS
->LocateProtocol (&gEfiSmmVariableProtocolGuid
, NULL
, (VOID
**)&mSmmVariable
);
586 if (EFI_ERROR (Status
)) {
590 Status
= gBS
->LocateProtocol (&gEfiSmmCommunicationProtocolGuid
, NULL
, (VOID
**) &mSmmCommunication
);
591 ASSERT_EFI_ERROR (Status
);
594 // Allocate memory for variable store.
596 mVariableBufferSize
= SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
597 mVariableBufferSize
+= MAX (PcdGet32 (PcdMaxVariableSize
), PcdGet32 (PcdMaxHardwareErrorVariableSize
));
598 mVariableBuffer
= AllocateRuntimePool (mVariableBufferSize
);
599 ASSERT (mVariableBuffer
!= NULL
);
602 // Save the buffer physical address used for SMM conmunication.
604 mVariableBufferPhysical
= mVariableBuffer
;
606 gRT
->GetVariable
= RuntimeServiceGetVariable
;
607 gRT
->GetNextVariableName
= RuntimeServiceGetNextVariableName
;
608 gRT
->SetVariable
= RuntimeServiceSetVariable
;
609 gRT
->QueryVariableInfo
= RuntimeServiceQueryVariableInfo
;
612 // Install the Variable Architectural Protocol on a new handle.
614 Status
= gBS
->InstallProtocolInterface (
616 &gEfiVariableArchProtocolGuid
,
617 EFI_NATIVE_INTERFACE
,
620 ASSERT_EFI_ERROR (Status
);
625 SMM Non-Volatile variable write service is ready notify event handler.
627 @param[in] Event Event whose notification function is being invoked.
628 @param[in] Context Pointer to the notification function's context.
633 SmmVariableWriteReady (
642 // Check whether the protocol is installed or not.
644 Status
= gBS
->LocateProtocol (&gSmmVariableWriteGuid
, NULL
, (VOID
**) &ProtocolOps
);
645 if (EFI_ERROR (Status
)) {
649 Status
= gBS
->InstallProtocolInterface (
651 &gEfiVariableWriteArchProtocolGuid
,
652 EFI_NATIVE_INTERFACE
,
655 ASSERT_EFI_ERROR (Status
);
660 Variable Driver main entry point. The Variable driver places the 4 EFI
661 runtime services in the EFI System Table and installs arch protocols
662 for variable read and write services being available. It also registers
663 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
665 @param[in] ImageHandle The firmware allocated handle for the EFI image.
666 @param[in] SystemTable A pointer to the EFI System Table.
668 @retval EFI_SUCCESS Variable service successfully initialized.
673 VariableSmmRuntimeInitialize (
674 IN EFI_HANDLE ImageHandle
,
675 IN EFI_SYSTEM_TABLE
*SystemTable
678 VOID
*SmmVariableRegistration
;
679 VOID
*SmmVariableWriteRegistration
;
680 EFI_EVENT OnReadyToBootEvent
;
681 EFI_EVENT ExitBootServiceEvent
;
683 EfiInitializeLock (&mVariableServicesLock
, TPL_NOTIFY
);
686 // Smm variable service is ready
688 EfiCreateProtocolNotifyEvent (
689 &gEfiSmmVariableProtocolGuid
,
693 &SmmVariableRegistration
697 // Smm Non-Volatile variable write service is ready
699 EfiCreateProtocolNotifyEvent (
700 &gSmmVariableWriteGuid
,
702 SmmVariableWriteReady
,
704 &SmmVariableWriteRegistration
708 // Register the event to reclaim variable for OS usage.
710 EfiCreateEventReadyToBootEx (
718 // Register the event to inform SMM variable that it is at runtime.
725 &gEfiEventExitBootServicesGuid
,
726 &ExitBootServiceEvent
730 // Register the event to convert the pointer for runtime.
735 VariableAddressChangeEvent
,
737 &gEfiEventVirtualAddressChangeGuid
,
738 &mVirtualAddressChangeEvent