2 Implement all four UEFI Runtime Variable services for the nonvolatile
3 and volatile storage space and install variable architecture protocol
4 based on SMM variable module.
6 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 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/AuthenticatedVariableFormat.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
;
48 Initialize the communicate buffer using DataSize and Function.
50 The communicate size is: SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE +
53 @param[out] DataPtr Points to the data in the communicate buffer.
54 @param[in] DataSize The data size to send to SMM.
55 @param[in] Function The function number to initialize the communicate header.
57 @retval EFI_INVALID_PARAMETER The data size is too big.
58 @retval EFI_SUCCESS Find the specified variable.
62 InitCommunicateBuffer (
63 OUT VOID
**DataPtr OPTIONAL
,
68 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
69 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
72 if (DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
> mVariableBufferSize
) {
73 return EFI_INVALID_PARAMETER
;
76 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
77 CopyGuid (&SmmCommunicateHeader
->HeaderGuid
, &gEfiSmmVariableProtocolGuid
);
78 SmmCommunicateHeader
->MessageLength
= DataSize
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
80 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*) SmmCommunicateHeader
->Data
;
81 SmmVariableFunctionHeader
->Function
= Function
;
82 if (DataPtr
!= NULL
) {
83 *DataPtr
= SmmVariableFunctionHeader
->Data
;
91 Send the data in communicate buffer to SMM.
93 @param[in] DataSize This size of the function header and the data.
95 @retval EFI_SUCCESS Success is returned from the functin in SMM.
96 @retval Others Failure is returned from the function in SMM.
100 SendCommunicateBuffer (
106 EFI_SMM_COMMUNICATE_HEADER
*SmmCommunicateHeader
;
107 SMM_VARIABLE_COMMUNICATE_HEADER
*SmmVariableFunctionHeader
;
109 CommSize
= DataSize
+ SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
110 Status
= mSmmCommunication
->Communicate (mSmmCommunication
, mVariableBufferPhysical
, &CommSize
);
111 ASSERT_EFI_ERROR (Status
);
113 SmmCommunicateHeader
= (EFI_SMM_COMMUNICATE_HEADER
*) mVariableBuffer
;
114 SmmVariableFunctionHeader
= (SMM_VARIABLE_COMMUNICATE_HEADER
*)SmmCommunicateHeader
->Data
;
115 return SmmVariableFunctionHeader
->ReturnStatus
;
120 This code finds variable in storage blocks (Volatile or Non-Volatile).
122 @param[in] VariableName Name of Variable to be found.
123 @param[in] VendorGuid Variable vendor GUID.
124 @param[out] Attributes Attribute value of the variable found.
125 @param[in, out] DataSize Size of Data found. If size is less than the
126 data, this value contains the required size.
127 @param[out] Data Data pointer.
129 @retval EFI_INVALID_PARAMETER Invalid parameter.
130 @retval EFI_SUCCESS Find the specified variable.
131 @retval EFI_NOT_FOUND Not found.
132 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
137 RuntimeServiceGetVariable (
138 IN CHAR16
*VariableName
,
139 IN EFI_GUID
*VendorGuid
,
140 OUT UINT32
*Attributes OPTIONAL
,
141 IN OUT UINTN
*DataSize
,
147 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
149 if (VariableName
== NULL
|| VendorGuid
== NULL
|| DataSize
== NULL
) {
150 return EFI_INVALID_PARAMETER
;
153 if ((*DataSize
!= 0) && (Data
== NULL
)) {
154 return EFI_INVALID_PARAMETER
;
158 // Init the communicate buffer. The buffer data size is:
159 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
161 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
);
162 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_VARIABLE
);
163 if (EFI_ERROR (Status
)) {
166 ASSERT (SmmVariableHeader
!= NULL
);
168 CopyGuid (&SmmVariableHeader
->Guid
, VendorGuid
);
169 SmmVariableHeader
->DataSize
= *DataSize
;
170 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
171 if (Attributes
== NULL
) {
172 SmmVariableHeader
->Attributes
= 0;
174 SmmVariableHeader
->Attributes
= *Attributes
;
176 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
181 Status
= SendCommunicateBuffer (PayloadSize
);
184 // Get data from SMM.
186 *DataSize
= SmmVariableHeader
->DataSize
;
187 if (Attributes
!= NULL
) {
188 *Attributes
= SmmVariableHeader
->Attributes
;
191 if (EFI_ERROR (Status
)) {
195 CopyMem (Data
, (UINT8
*)SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, SmmVariableHeader
->DataSize
);
202 This code Finds the Next available variable.
204 @param[in, out] VariableNameSize Size of the variable name.
205 @param[in, out] VariableName Pointer to variable name.
206 @param[in, out] VendorGuid Variable Vendor Guid.
208 @retval EFI_INVALID_PARAMETER Invalid parameter.
209 @retval EFI_SUCCESS Find the specified variable.
210 @retval EFI_NOT_FOUND Not found.
211 @retval EFI_BUFFER_TO_SMALL DataSize is too small for the result.
216 RuntimeServiceGetNextVariableName (
217 IN OUT UINTN
*VariableNameSize
,
218 IN OUT CHAR16
*VariableName
,
219 IN OUT EFI_GUID
*VendorGuid
224 SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
*SmmGetNextVariableName
;
226 if (VariableNameSize
== NULL
|| VariableName
== NULL
|| VendorGuid
== NULL
) {
227 return EFI_INVALID_PARAMETER
;
231 // Init the communicate buffer. The buffer data size is:
232 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
234 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME
, Name
) + *VariableNameSize
;
235 Status
= InitCommunicateBuffer ((VOID
**)&SmmGetNextVariableName
, PayloadSize
, SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME
);
236 if (EFI_ERROR (Status
)) {
239 ASSERT (SmmGetNextVariableName
!= NULL
);
241 SmmGetNextVariableName
->NameSize
= *VariableNameSize
;
242 CopyGuid (&SmmGetNextVariableName
->Guid
, VendorGuid
);
243 CopyMem (SmmGetNextVariableName
->Name
, VariableName
, *VariableNameSize
);
248 Status
= SendCommunicateBuffer (PayloadSize
);
251 // Get data from SMM.
253 *VariableNameSize
= SmmGetNextVariableName
->NameSize
;
254 if (EFI_ERROR (Status
)) {
258 CopyGuid (VendorGuid
, &SmmGetNextVariableName
->Guid
);
259 CopyMem (VariableName
, SmmGetNextVariableName
->Name
, SmmGetNextVariableName
->NameSize
);
265 This code sets variable in storage blocks (Volatile or Non-Volatile).
267 @param[in] VariableName Name of Variable to be found.
268 @param[in] VendorGuid Variable vendor GUID.
269 @param[in] Attributes Attribute value of the variable found
270 @param[in] DataSize Size of Data found. If size is less than the
271 data, this value contains the required size.
272 @param[in] Data Data pointer.
274 @retval EFI_INVALID_PARAMETER Invalid parameter.
275 @retval EFI_SUCCESS Set successfully.
276 @retval EFI_OUT_OF_RESOURCES Resource not enough to set variable.
277 @retval EFI_NOT_FOUND Not found.
278 @retval EFI_WRITE_PROTECTED Variable is read-only.
283 RuntimeServiceSetVariable (
284 IN CHAR16
*VariableName
,
285 IN EFI_GUID
*VendorGuid
,
286 IN UINT32 Attributes
,
293 SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
*SmmVariableHeader
;
296 // Check input parameters.
298 if (VariableName
== NULL
|| VariableName
[0] == 0 || VendorGuid
== NULL
) {
299 return EFI_INVALID_PARAMETER
;
302 if (DataSize
!= 0 && Data
== NULL
) {
303 return EFI_INVALID_PARAMETER
;
307 // Init the communicate buffer. The buffer data size is:
308 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize.
310 PayloadSize
= OFFSET_OF (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE
, Name
) + StrSize (VariableName
) + DataSize
;
311 Status
= InitCommunicateBuffer ((VOID
**)&SmmVariableHeader
, PayloadSize
, SMM_VARIABLE_FUNCTION_SET_VARIABLE
);
312 if (EFI_ERROR (Status
)) {
315 ASSERT (SmmVariableHeader
!= NULL
);
317 CopyGuid ((EFI_GUID
*) &SmmVariableHeader
->Guid
, VendorGuid
);
318 SmmVariableHeader
->DataSize
= DataSize
;
319 SmmVariableHeader
->NameSize
= StrSize (VariableName
);
320 SmmVariableHeader
->Attributes
= Attributes
;
321 CopyMem (SmmVariableHeader
->Name
, VariableName
, SmmVariableHeader
->NameSize
);
322 CopyMem ((UINT8
*) SmmVariableHeader
->Name
+ SmmVariableHeader
->NameSize
, Data
, DataSize
);
327 Status
= SendCommunicateBuffer (PayloadSize
);
334 This code returns information about the EFI variables.
336 @param[in] Attributes Attributes bitmask to specify the type of variables
337 on which to return information.
338 @param[out] MaximumVariableStorageSize Pointer to the maximum size of the storage space available
339 for the EFI variables associated with the attributes specified.
340 @param[out] RemainingVariableStorageSize Pointer to the remaining size of the storage space available
341 for EFI variables associated with the attributes specified.
342 @param[out] MaximumVariableSize Pointer to the maximum size of an individual EFI variables
343 associated with the attributes specified.
345 @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits was supplied.
346 @retval EFI_SUCCESS Query successfully.
347 @retval EFI_UNSUPPORTED The attribute is not supported on this platform.
352 RuntimeServiceQueryVariableInfo (
353 IN UINT32 Attributes
,
354 OUT UINT64
*MaximumVariableStorageSize
,
355 OUT UINT64
*RemainingVariableStorageSize
,
356 OUT UINT64
*MaximumVariableSize
361 SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
*SmmQueryVariableInfo
;
363 if(MaximumVariableStorageSize
== NULL
|| RemainingVariableStorageSize
== NULL
|| MaximumVariableSize
== NULL
|| Attributes
== 0) {
364 return EFI_INVALID_PARAMETER
;
368 // Init the communicate buffer. The buffer data size is:
369 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE + PayloadSize;
371 PayloadSize
= sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO
);
372 Status
= InitCommunicateBuffer ((VOID
**)&SmmQueryVariableInfo
, PayloadSize
, SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO
);
373 if (EFI_ERROR (Status
)) {
376 ASSERT (SmmQueryVariableInfo
!= NULL
);
378 SmmQueryVariableInfo
->Attributes
= Attributes
;
383 Status
= SendCommunicateBuffer (PayloadSize
);
384 if (EFI_ERROR (Status
)) {
389 // Get data from SMM.
391 *MaximumVariableSize
= SmmQueryVariableInfo
->MaximumVariableSize
;
392 *MaximumVariableStorageSize
= SmmQueryVariableInfo
->MaximumVariableStorageSize
;
393 *RemainingVariableStorageSize
= SmmQueryVariableInfo
->RemainingVariableStorageSize
;
400 Exit Boot Services Event notification handler.
402 Notify SMM variable driver about the event.
404 @param[in] Event Event whose notification function is being invoked.
405 @param[in] Context Pointer to the notification function's context.
416 // Init the communicate buffer. The buffer data size is:
417 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
419 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE
);
424 SendCommunicateBuffer (0);
429 On Ready To Boot Services Event notification handler.
431 Notify SMM variable driver about the event.
433 @param[in] Event Event whose notification function is being invoked
434 @param[in] Context Pointer to the notification function's context
445 // Init the communicate buffer. The buffer data size is:
446 // SMM_COMMUNICATE_HEADER_SIZE + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE.
448 InitCommunicateBuffer (NULL
, 0, SMM_VARIABLE_FUNCTION_READY_TO_BOOT
);
453 SendCommunicateBuffer (0);
458 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
460 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
461 It convers pointer to new virtual address.
463 @param[in] Event Event whose notification function is being invoked.
464 @param[in] Context Pointer to the notification function's context.
469 VariableAddressChangeEvent (
474 EfiConvertPointer (0x0, (VOID
**) &mVariableBuffer
);
475 EfiConvertPointer (0x0, (VOID
**) &mSmmCommunication
);
480 Initialize variable service and install Variable Architectural protocol.
482 @param[in] Event Event whose notification function is being invoked.
483 @param[in] Context Pointer to the notification function's context.
495 Status
= gBS
->LocateProtocol (&gEfiSmmVariableProtocolGuid
, NULL
, (VOID
**)&mSmmVariable
);
496 if (EFI_ERROR (Status
)) {
500 Status
= gBS
->LocateProtocol (&gEfiSmmCommunicationProtocolGuid
, NULL
, (VOID
**) &mSmmCommunication
);
501 ASSERT_EFI_ERROR (Status
);
504 // Allocate memory for variable store.
506 mVariableBufferSize
= SMM_COMMUNICATE_HEADER_SIZE
+ SMM_VARIABLE_COMMUNICATE_HEADER_SIZE
;
507 mVariableBufferSize
+= MAX (PcdGet32 (PcdMaxVariableSize
), PcdGet32 (PcdMaxHardwareErrorVariableSize
));
508 mVariableBuffer
= AllocateRuntimePool (mVariableBufferSize
);
509 ASSERT (mVariableBuffer
!= NULL
);
512 // Save the buffer physical address used for SMM conmunication.
514 mVariableBufferPhysical
= mVariableBuffer
;
516 gRT
->GetVariable
= RuntimeServiceGetVariable
;
517 gRT
->GetNextVariableName
= RuntimeServiceGetNextVariableName
;
518 gRT
->SetVariable
= RuntimeServiceSetVariable
;
519 gRT
->QueryVariableInfo
= RuntimeServiceQueryVariableInfo
;
522 // Install the Variable Architectural Protocol on a new handle.
524 Status
= gBS
->InstallProtocolInterface (
526 &gEfiVariableArchProtocolGuid
,
527 EFI_NATIVE_INTERFACE
,
530 ASSERT_EFI_ERROR (Status
);
535 SMM Non-Volatile variable write service is ready notify event handler.
537 @param[in] Event Event whose notification function is being invoked.
538 @param[in] Context Pointer to the notification function's context.
543 SmmVariableWriteReady (
552 // Check whether the protocol is installed or not.
554 Status
= gBS
->LocateProtocol (&gSmmVariableWriteGuid
, NULL
, (VOID
**) &ProtocolOps
);
555 if (EFI_ERROR (Status
)) {
559 Status
= gBS
->InstallProtocolInterface (
561 &gEfiVariableWriteArchProtocolGuid
,
562 EFI_NATIVE_INTERFACE
,
565 ASSERT_EFI_ERROR (Status
);
570 Variable Driver main entry point. The Variable driver places the 4 EFI
571 runtime services in the EFI System Table and installs arch protocols
572 for variable read and write services being available. It also registers
573 a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
575 @param[in] ImageHandle The firmware allocated handle for the EFI image.
576 @param[in] SystemTable A pointer to the EFI System Table.
578 @retval EFI_SUCCESS Variable service successfully initialized.
583 VariableSmmRuntimeInitialize (
584 IN EFI_HANDLE ImageHandle
,
585 IN EFI_SYSTEM_TABLE
*SystemTable
588 VOID
*SmmVariableRegistration
;
589 VOID
*SmmVariableWriteRegistration
;
590 EFI_EVENT OnReadyToBootEvent
;
591 EFI_EVENT ExitBootServiceEvent
;
594 // Smm variable service is ready
596 EfiCreateProtocolNotifyEvent (
597 &gEfiSmmVariableProtocolGuid
,
601 &SmmVariableRegistration
605 // Smm Non-Volatile variable write service is ready
607 EfiCreateProtocolNotifyEvent (
608 &gSmmVariableWriteGuid
,
610 SmmVariableWriteReady
,
612 &SmmVariableWriteRegistration
616 // Register the event to reclaim variable for OS usage.
618 EfiCreateEventReadyToBootEx (
626 // Register the event to inform SMM variable that it is at runtime.
633 &gEfiEventExitBootServicesGuid
,
634 &ExitBootServiceEvent
638 // Register the event to convert the pointer for runtime.
643 VariableAddressChangeEvent
,
645 &gEfiEventVirtualAddressChangeGuid
,
646 &mVirtualAddressChangeEvent