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 - 2011, 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
;
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