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, 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>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/UefiRuntimeServicesTableLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/UefiDriverEntryPoint.h>
26 #include <Library/UefiRuntimeLib.h>
27 #include <Library/BaseMemoryLib.h>
28 #include <Library/DebugLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/UefiLib.h>
31 #include <Library/BaseLib.h>
33 #include <Guid/EventGroup.h>
34 #include "VariableSmmCommon.h"
36 EFI_HANDLE mHandle
= NULL
;
37 EFI_SMM_VARIABLE_PROTOCOL
*mSmmVariable
= NULL
;
38 EFI_EVENT mVirtualAddressChangeEvent
= NULL
;
39 EFI_SMM_COMMUNICATION_PROTOCOL
*mSmmCommunication
= NULL
;
40 UINT8
*mVariableBuffer
= NULL
;
41 UINT8
*mVariableBufferPhysical
= NULL
;
42 EFI_GUID mSmmVariableWriteGuid
= EFI_SMM_VARIABLE_WRITE_GUID
;
43 UINTN mVariableBufferSize
;
47 Initialize the communicate buffer using DataSize and Function.
49 The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
52 @param[out] DataPtr Points to the data in the communicate buffer.
53 @param[in] DataSize The data size to send to SMM.
54 @param[in] Function The function number to initialize the communicate header.
56 @retval EFI_INVALID_PARAMETER The data size is too big.
57 @retval EFI_SUCCESS Find the specified variable.
61 InitCommunicateBuffer (
62 OUT VOID
**DataPtr OPTIONAL
,
67 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
68 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
71 if (DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
> mVariableBufferSize
) {
72 return EFI_INVALID_PARAMETER
;
75 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
76 CopyGuid (&SmmCommunicateHeader
->HeaderGuid
, &gEfiSmmVariableProtocolGuid
);
77 SmmCommunicateHeader
->MessageLength
= DataSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
79 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*) SmmCommunicateHeader
->Data
;
80 SmmVariableFunctionHeader
->Function
= Function
;
81 if (DataPtr
!= NULL
) {
82 *DataPtr
= SmmVariableFunctionHeader
->Data
;
90 Send the data in communicate buffer to SMM.
92 @param[in] DataSize This size of the function header and the data.
94 @retval EFI_SUCCESS Success is returned from the functin in SMM.
95 @retval Others Failure is returned from the function in SMM.
99 SendCommunicateBuffer (
105 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
106 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
108 CommSize
= DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
109 Status
= mSmmCommunication
->Communicate (mSmmCommunication
, mVariableBufferPhysical
, &CommSize
);
110 ASSERT_EFI_ERROR (Status
);
112 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
113 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)SmmCommunicateHeader
->Data
;
114 return SmmVariableFunctionHeader
->ReturnStatus
;
119 This code finds variable in storage blocks (Volatile or Non-Volatile).
121 @param[in] VariableName Name of Variable to be found.
122 @param[in] VendorGuid Variable vendor GUID.
123 @param[out] Attributes Attribute value of the variable found.
124 @param[in, out] DataSize Size of Data found. If size is less than the
125 data, this value contains the required size.
126 @param[out] Data Data pointer.
128 @retval EFI_INVALID_PARAMETER Invalid parameter.
129 @retval EFI_SUCCESS Find the specified variable.
130 @retval EFI_NOT_FOUND Not found.
131 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
136 RuntimeServiceGetVariable (
137 IN CHAR16
*VariableName
,
138 IN EFI_GUID
*VendorGuid
,
139 OUT UINT32
*Attributes OPTIONAL
,
140 IN OUT UINTN
*DataSize
,
146 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
148 if (VariableName
== NULL
|| VendorGuid
== NULL
|| DataSize
== NULL
) {
149 return EFI_INVALID_PARAMETER
;
152 if ((*DataSize
!= 0) && (Data
== NULL
)) {
153 return EFI_INVALID_PARAMETER
;
157 // Init the communicate buffer. The buffer data size is:
158 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
160 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
);
161 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_VARIABLE
);
162 if (EFI_ERROR (Status
)) {
165 ASSERT (SmmVariableHeader
!= NULL
);
167 CopyGuid (&SmmVariableHeader
->Guid
, VendorGuid
);
168 SmmVariableHeader
->DataSize
= *DataSize
;
169 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
170 if (Attributes
== NULL
) {
171 SmmVariableHeader
->Attributes
= 0;
173 SmmVariableHeader
->Attributes
= *Attributes
;
175 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
180 Status
= SendCommunicateBuffer (PayloadSize
);
183 // Get data from SMM.
185 *DataSize
= SmmVariableHeader
->DataSize
;
186 if (Attributes
!= NULL
) {
187 *Attributes
= SmmVariableHeader
->Attributes
;
190 if (EFI_ERROR (Status
)) {
194 CopyMem (Data
, (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, SmmVariableHeader
->DataSize
);
201 This code Finds the Next available variable.
203 @param[in, out] VariableNameSize Size of the variable name.
204 @param[in, out] VariableName Pointer to variable name.
205 @param[in, out] VendorGuid Variable Vendor Guid.
207 @retval EFI_INVALID_PARAMETER Invalid parameter.
208 @retval EFI_SUCCESS Find the specified variable.
209 @retval EFI_NOT_FOUND Not found.
210 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
215 RuntimeServiceGetNextVariableName (
216 IN OUT UINTN
*VariableNameSize
,
217 IN OUT CHAR16
*VariableName
,
218 IN OUT EFI_GUID
*VendorGuid
223 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*SmmGetNextVariableName
;
225 if (VariableNameSize
== NULL
|| VariableName
== NULL
|| VendorGuid
== NULL
) {
226 return EFI_INVALID_PARAMETER
;
230 // Init the communicate buffer. The buffer data size is:
231 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
233 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + *VariableNameSize
;
234 Status
= InitCommunicateBuffer ((VOID
**)&SmmGetNextVariableName
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
);
235 if (EFI_ERROR (Status
)) {
238 ASSERT (SmmGetNextVariableName
!= NULL
);
240 SmmGetNextVariableName
->NameSize
= *VariableNameSize
;
241 CopyGuid (&SmmGetNextVariableName
->Guid
, VendorGuid
);
242 CopyMem (SmmGetNextVariableName
->Name
, VariableName
, *VariableNameSize
);
247 Status
= SendCommunicateBuffer (PayloadSize
);
250 // Get data from SMM.
252 *VariableNameSize
= SmmGetNextVariableName
->NameSize
;
253 if (EFI_ERROR (Status
)) {
257 CopyGuid (VendorGuid
, &SmmGetNextVariableName
->Guid
);
258 CopyMem (VariableName
, SmmGetNextVariableName
->Name
, SmmGetNextVariableName
->NameSize
);
264 This code sets variable in storage blocks (Volatile or Non-Volatile).
266 @param[in] VariableName Name of Variable to be found.
267 @param[in] VendorGuid Variable vendor GUID.
268 @param[in] Attributes Attribute value of the variable found
269 @param[in] DataSize Size of Data found. If size is less than the
270 data, this value contains the required size.
271 @param[in] Data Data pointer.
273 @retval EFI_INVALID_PARAMETER Invalid parameter.
274 @retval EFI_SUCCESS Set successfully.
275 @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
276 @retval EFI_NOT_FOUND Not found.
277 @retval EFI_WRITE_PROTECTED Variable is read-only.
282 RuntimeServiceSetVariable (
283 IN CHAR16
*VariableName
,
284 IN EFI_GUID
*VendorGuid
,
285 IN UINT32 Attributes
,
292 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
295 // Check input parameters.
297 if (VariableName
== NULL
|| VariableName
[0] == 0 || VendorGuid
== NULL
) {
298 return EFI_INVALID_PARAMETER
;
301 if (DataSize
!= 0 && Data
== NULL
) {
302 return EFI_INVALID_PARAMETER
;
306 // Init the communicate buffer. The buffer data size is:
307 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
309 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
) + DataSize
;
310 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_SET_VARIABLE
);
311 if (EFI_ERROR (Status
)) {
314 ASSERT (SmmVariableHeader
!= NULL
);
316 CopyGuid ((EFI_GUID
*) &SmmVariableHeader
->Guid
, VendorGuid
);
317 SmmVariableHeader
->DataSize
= DataSize
;
318 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
319 SmmVariableHeader
->Attributes
= Attributes
;
320 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
321 CopyMem ((UINT8
*) SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, Data
, DataSize
);
326 Status
= SendCommunicateBuffer (PayloadSize
);
333 This code returns information about the EFI variables.
335 @param[in] Attributes Attributes bitmask to specify the type of variables
336 on which to return information.
337 @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available
338 for the EFI variables associated with the attributes specified.
339 @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available
340 for EFI variables associated with the attributes specified.
341 @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables
342 associated with the attributes specified.
344 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.
345 @retval EFI_SUCCESS Query successfully.
346 @retval EFI_UNSUPPORTED The attribute is not supported on this platform.
351 RuntimeServiceQueryVariableInfo (
352 IN UINT32 Attributes
,
353 OUT UINT64
*MaximumVariableStorageSize
,
354 OUT UINT64
*RemainingVariableStorageSize
,
355 OUT UINT64
*MaximumVariableSize
360 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*SmmQueryVariableInfo
;
362 if(MaximumVariableStorageSize
== NULL
|| RemainingVariableStorageSize
== NULL
|| MaximumVariableSize
== NULL
|| Attributes
== 0) {
363 return EFI_INVALID_PARAMETER
;
367 // Init the communicate buffer. The buffer data size is:
368 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize;
370 PayloadSize
= sizeof (SMM_VARIABLE_COMMUNICATE_VARIABLE_INFO_ENTRY
);
371 Status
= InitCommunicateBuffer ((VOID
**)&SmmQueryVariableInfo
, PayloadSize
, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
);
372 if (EFI_ERROR (Status
)) {
375 ASSERT (SmmQueryVariableInfo
!= NULL
);
377 SmmQueryVariableInfo
->Attributes
= Attributes
;
382 Status
= SendCommunicateBuffer (PayloadSize
);
383 if (EFI_ERROR (Status
)) {
388 // Get data from SMM.
390 *MaximumVariableSize
= SmmQueryVariableInfo
->MaximumVariableSize
;
391 *MaximumVariableStorageSize
= SmmQueryVariableInfo
->MaximumVariableStorageSize
;
392 *RemainingVariableStorageSize
= SmmQueryVariableInfo
->RemainingVariableStorageSize
;
399 Exit Boot Services Event notification handler.
401 Notify SMM variable driver about the event.
403 @param[in] Event Event whose notification function is being invoked.
404 @param[in] Context Pointer to the notification function's context.
415 // Init the communicate buffer. The buffer data size is:
416 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
418 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
);
423 SendCommunicateBuffer (0);
428 On Ready To Boot Services Event notification handler.
430 Notify SMM variable driver about the event.
432 @param[in] Event Event whose notification function is being invoked
433 @param[in] Context Pointer to the notification function's context
444 // Init the communicate buffer. The buffer data size is:
445 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
447 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT
);
452 SendCommunicateBuffer (0);
457 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
459 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
460 It convers pointer to new virtual address.
462 @param[in] Event Event whose notification function is being invoked.
463 @param[in] Context Pointer to the notification function's context.
468 VariableAddressChangeEvent (
473 EfiConvertPointer (0x0, (VOID
**) &mVariableBuffer
);
474 EfiConvertPointer (0x0, (VOID
**) &mSmmCommunication
);
479 Initialize variable service and install Variable Architectural protocol.
481 @param[in] Event Event whose notification function is being invoked.
482 @param[in] Context Pointer to the notification function's context.
494 Status
= gBS
->LocateProtocol (&gEfiSmmVariableProtocolGuid
, NULL
, (VOID
**)&mSmmVariable
);
495 if (EFI_ERROR (Status
)) {
499 Status
= gBS
->LocateProtocol (&gEfiSmmCommunicationProtocolGuid
, NULL
, (VOID
**) &mSmmCommunication
);
500 ASSERT_EFI_ERROR (Status
);
503 // Allocate memory for variable store.
505 mVariableBufferSize
= SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
506 mVariableBufferSize
+= MAX (PcdGet32 (PcdMaxVariableSize
), PcdGet32 (PcdMaxHardwareErrorVariableSize
));
507 mVariableBuffer
= AllocateRuntimePool (mVariableBufferSize
);
508 ASSERT (mVariableBuffer
!= NULL
);
511 // Save the buffer physical address used for SMM conmunication.
513 mVariableBufferPhysical
= mVariableBuffer
;
515 gRT
->GetVariable
= RuntimeServiceGetVariable
;
516 gRT
->GetNextVariableName
= RuntimeServiceGetNextVariableName
;
517 gRT
->SetVariable
= RuntimeServiceSetVariable
;
518 gRT
->QueryVariableInfo
= RuntimeServiceQueryVariableInfo
;
521 // Install the Variable Architectural Protocol on a new handle.
523 Status
= gBS
->InstallProtocolInterface (
525 &gEfiVariableArchProtocolGuid
,
526 EFI_NATIVE_INTERFACE
,
529 ASSERT_EFI_ERROR (Status
);
534 SMM Non-Volatile variable write service is ready notify event handler.
536 @param[in] Event Event whose notification function is being invoked.
537 @param[in] Context Pointer to the notification function's context.
542 SmmVariableWriteReady (
551 // Check whether the protocol is installed or not.
553 Status
= gBS
->LocateProtocol (&mSmmVariableWriteGuid
, NULL
, (VOID
**) &ProtocolOps
);
554 if (EFI_ERROR (Status
)) {
558 Status
= gBS
->InstallProtocolInterface (
560 &gEfiVariableWriteArchProtocolGuid
,
561 EFI_NATIVE_INTERFACE
,
564 ASSERT_EFI_ERROR (Status
);
569 Variable Driver main entry point. The Variable driver places the 4 EFI
570 runtime services in the EFI System Table and installs arch protocols
571 for variable read and write services being availible. It also registers
572 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
574 @param[in] ImageHandle The firmware allocated handle for the EFI image.
575 @param[in] SystemTable A pointer to the EFI System Table.
577 @retval EFI_SUCCESS Variable service successfully initialized.
582 VariableSmmRuntimeInitialize (
583 IN EFI_HANDLE ImageHandle
,
584 IN EFI_SYSTEM_TABLE
*SystemTable
587 VOID
*SmmVariableRegistration
;
588 VOID
*SmmVariableWriteRegistration
;
589 EFI_EVENT OnReadyToBootEvent
;
590 EFI_EVENT ExitBootServiceEvent
;
593 // Smm variable service is ready
595 EfiCreateProtocolNotifyEvent (
596 &gEfiSmmVariableProtocolGuid
,
600 &SmmVariableRegistration
604 // Smm Non-Volatile variable write service is ready
606 EfiCreateProtocolNotifyEvent (
607 &mSmmVariableWriteGuid
,
609 SmmVariableWriteReady
,
611 &SmmVariableWriteRegistration
615 // Register the event to reclaim variable for OS usage.
617 EfiCreateEventReadyToBootEx (
625 // Register the event to inform SMM variable that it is at runtime.
632 &gEfiEventExitBootServicesGuid
,
633 &ExitBootServiceEvent
637 // Register the event to convert the pointer for runtime.
642 VariableAddressChangeEvent
,
644 &gEfiEventVirtualAddressChangeGuid
,
645 &mVirtualAddressChangeEvent