1/** @file
2  Implement all four UEFI Runtime Variable services for the nonvolatile
3  and volatile storage space and install variable architecture protocol.
4
5Copyright (C) 2013, Red Hat, Inc.
6Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
7(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
8This program and the accompanying materials
9are licensed and made available under the terms and conditions of the BSD License
10which accompanies this distribution.  The full text of the license may be found at
11http://opensource.org/licenses/bsd-license.php
12
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15
16**/
17
18#include "Variable.h"
19
20extern VARIABLE_STORE_HEADER        *mNvVariableCache;
21extern EFI_FIRMWARE_VOLUME_HEADER   *mNvFvHeaderCache;
22extern VARIABLE_INFO_ENTRY          *gVariableInfo;
23EFI_HANDLE                          mHandle                    = NULL;
24EFI_EVENT                           mVirtualAddressChangeEvent = NULL;
25EFI_EVENT                           mFtwRegistration           = NULL;
26extern BOOLEAN                      mEndOfDxe;
27VOID                                ***mVarCheckAddressPointer = NULL;
28UINTN                               mVarCheckAddressPointerCount = 0;
29EDKII_VARIABLE_LOCK_PROTOCOL        mVariableLock              = { VariableLockRequestToLock };
30EDKII_VAR_CHECK_PROTOCOL            mVarCheck                  = { VarCheckRegisterSetVariableCheckHandler,
31                                                                    VarCheckVariablePropertySet,
32                                                                    VarCheckVariablePropertyGet };
33
34/**
35  Return TRUE if ExitBootServices () has been called.
36
37  @retval TRUE If ExitBootServices () has been called.
38**/
39BOOLEAN
40AtRuntime (
41  VOID
42  )
43{
44  return EfiAtRuntime ();
45}
46
47
48/**
49  Initializes a basic mutual exclusion lock.
50
51  This function initializes a basic mutual exclusion lock to the released state
52  and returns the lock.  Each lock provides mutual exclusion access at its task
53  priority level.  Since there is no preemption or multiprocessor support in EFI,
54  acquiring the lock only consists of raising to the locks TPL.
55  If Lock is NULL, then ASSERT().
56  If Priority is not a valid TPL value, then ASSERT().
57
58  @param  Lock       A pointer to the lock data structure to initialize.
59  @param  Priority   EFI TPL is associated with the lock.
60
61  @return The lock.
62
63**/
64EFI_LOCK *
65InitializeLock (
66  IN OUT EFI_LOCK                         *Lock,
67  IN     EFI_TPL                          Priority
68  )
69{
70  return EfiInitializeLock (Lock, Priority);
71}
72
73
74/**
75  Acquires lock only at boot time. Simply returns at runtime.
76
77  This is a temperary function that will be removed when
78  EfiAcquireLock() in UefiLib can handle the call in UEFI
79  Runtimer driver in RT phase.
80  It calls EfiAcquireLock() at boot time, and simply returns
81  at runtime.
82
83  @param  Lock         A pointer to the lock to acquire.
84
85**/
86VOID
87AcquireLockOnlyAtBootTime (
88  IN EFI_LOCK                             *Lock
89  )
90{
91  if (!AtRuntime ()) {
92    EfiAcquireLock (Lock);
93  }
94}
95
96
97/**
98  Releases lock only at boot time. Simply returns at runtime.
99
100  This is a temperary function which will be removed when
101  EfiReleaseLock() in UefiLib can handle the call in UEFI
102  Runtimer driver in RT phase.
103  It calls EfiReleaseLock() at boot time and simply returns
104  at runtime.
105
106  @param  Lock         A pointer to the lock to release.
107
108**/
109VOID
110ReleaseLockOnlyAtBootTime (
111  IN EFI_LOCK                             *Lock
112  )
113{
114  if (!AtRuntime ()) {
115    EfiReleaseLock (Lock);
116  }
117}
118
119/**
120  Retrive the Fault Tolerent Write protocol interface.
121
122  @param[out] FtwProtocol       The interface of Ftw protocol
123
124  @retval EFI_SUCCESS           The FTW protocol instance was found and returned in FtwProtocol.
125  @retval EFI_NOT_FOUND         The FTW protocol instance was not found.
126  @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
127
128**/
129EFI_STATUS
130GetFtwProtocol (
131  OUT VOID                                **FtwProtocol
132  )
133{
134  EFI_STATUS                              Status;
135
136  //
137  // Locate Fault Tolerent Write protocol
138  //
139  Status = gBS->LocateProtocol (
140                  &gEfiFaultTolerantWriteProtocolGuid,
141                  NULL,
142                  FtwProtocol
143                  );
144  return Status;
145}
146
147/**
148  Retrive the FVB protocol interface by HANDLE.
149
150  @param[in]  FvBlockHandle     The handle of FVB protocol that provides services for
151                                reading, writing, and erasing the target block.
152  @param[out] FvBlock           The interface of FVB protocol
153
154  @retval EFI_SUCCESS           The interface information for the specified protocol was returned.
155  @retval EFI_UNSUPPORTED       The device does not support the FVB protocol.
156  @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
157
158**/
159EFI_STATUS
160GetFvbByHandle (
161  IN  EFI_HANDLE                          FvBlockHandle,
162  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock
163  )
164{
165  //
166  // To get the FVB protocol interface on the handle
167  //
168  return gBS->HandleProtocol (
169                FvBlockHandle,
170                &gEfiFirmwareVolumeBlockProtocolGuid,
171                (VOID **) FvBlock
172                );
173}
174
175
176/**
177  Function returns an array of handles that support the FVB protocol
178  in a buffer allocated from pool.
179
180  @param[out]  NumberHandles    The number of handles returned in Buffer.
181  @param[out]  Buffer           A pointer to the buffer to return the requested
182                                array of  handles that support FVB protocol.
183
184  @retval EFI_SUCCESS           The array of handles was returned in Buffer, and the number of
185                                handles in Buffer was returned in NumberHandles.
186  @retval EFI_NOT_FOUND         No FVB handle was found.
187  @retval EFI_OUT_OF_RESOURCES  There is not enough pool memory to store the matching results.
188  @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
189
190**/
191EFI_STATUS
192GetFvbCountAndBuffer (
193  OUT UINTN                               *NumberHandles,
194  OUT EFI_HANDLE                          **Buffer
195  )
196{
197  EFI_STATUS                              Status;
198
199  //
200  // Locate all handles of Fvb protocol
201  //
202  Status = gBS->LocateHandleBuffer (
203                  ByProtocol,
204                  &gEfiFirmwareVolumeBlockProtocolGuid,
205                  NULL,
206                  NumberHandles,
207                  Buffer
208                  );
209  return Status;
210}
211
212
213/**
214  Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
215
216  This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
217  It convers pointer to new virtual address.
218
219  @param  Event        Event whose notification function is being invoked.
220  @param  Context      Pointer to the notification function's context.
221
222**/
223VOID
224EFIAPI
225VariableClassAddressChangeEvent (
226  IN EFI_EVENT                            Event,
227  IN VOID                                 *Context
228  )
229{
230  UINTN          Index;
231
232  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetBlockSize);
233  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetPhysicalAddress);
234  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->GetAttributes);
235  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->SetAttributes);
236  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Read);
237  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->Write);
238  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance->EraseBlocks);
239  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->FvbInstance);
240  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLangCodes);
241  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->LangCodes);
242  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->PlatformLang);
243  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase);
244  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.VolatileVariableBase);
245  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal->VariableGlobal.HobVariableBase);
246  EfiConvertPointer (0x0, (VOID **) &mVariableModuleGlobal);
247  EfiConvertPointer (0x0, (VOID **) &mNvVariableCache);
248  EfiConvertPointer (0x0, (VOID **) &mNvFvHeaderCache);
249
250  if (mAuthContextOut.AddressPointer != NULL) {
251    for (Index = 0; Index < mAuthContextOut.AddressPointerCount; Index++) {
252      EfiConvertPointer (0x0, (VOID **) mAuthContextOut.AddressPointer[Index]);
253    }
254  }
255
256  if (mVarCheckAddressPointer != NULL) {
257    for (Index = 0; Index < mVarCheckAddressPointerCount; Index++) {
258      EfiConvertPointer (0x0, (VOID **) mVarCheckAddressPointer[Index]);
259    }
260  }
261}
262
263
264/**
265  Notification function of EVT_GROUP_READY_TO_BOOT event group.
266
267  This is a notification function registered on EVT_GROUP_READY_TO_BOOT event group.
268  When the Boot Manager is about to load and execute a boot option, it reclaims variable
269  storage if free size is below the threshold.
270
271  @param  Event        Event whose notification function is being invoked.
272  @param  Context      Pointer to the notification function's context.
273
274**/
275VOID
276EFIAPI
277OnReadyToBoot (
278  EFI_EVENT                               Event,
279  VOID                                    *Context
280  )
281{
282  if (!mEndOfDxe) {
283    //
284    // Set the End Of DXE bit in case the EFI_END_OF_DXE_EVENT_GROUP_GUID event is not signaled.
285    //
286    mEndOfDxe = TRUE;
287    mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount);
288    //
289    // The initialization for variable quota.
290    //
291    InitializeVariableQuota ();
292  }
293  ReclaimForOS ();
294  if (FeaturePcdGet (PcdVariableCollectStatistics)) {
295    if (mVariableModuleGlobal->VariableGlobal.AuthFormat) {
296      gBS->InstallConfigurationTable (&gEfiAuthenticatedVariableGuid, gVariableInfo);
297    } else {
298      gBS->InstallConfigurationTable (&gEfiVariableGuid, gVariableInfo);
299    }
300  }
301
302  gBS->CloseEvent (Event);
303}
304
305/**
306  Notification function of EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
307
308  This is a notification function registered on EFI_END_OF_DXE_EVENT_GROUP_GUID event group.
309
310  @param  Event        Event whose notification function is being invoked.
311  @param  Context      Pointer to the notification function's context.
312
313**/
314VOID
315EFIAPI
316OnEndOfDxe (
317  EFI_EVENT                               Event,
318  VOID                                    *Context
319  )
320{
321  DEBUG ((EFI_D_INFO, "[Variable]END_OF_DXE is signaled\n"));
322  mEndOfDxe = TRUE;
323  mVarCheckAddressPointer = VarCheckLibInitializeAtEndOfDxe (&mVarCheckAddressPointerCount);
324  //
325  // The initialization for variable quota.
326  //
327  InitializeVariableQuota ();
328  if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) {
329    ReclaimForOS ();
330  }
331
332  gBS->CloseEvent (Event);
333}
334
335/**
336  Fault Tolerant Write protocol notification event handler.
337
338  Non-Volatile variable write may needs FTW protocol to reclaim when
339  writting variable.
340
341  @param[in] Event    Event whose notification function is being invoked.
342  @param[in] Context  Pointer to the notification function's context.
343
344**/
345VOID
346EFIAPI
347FtwNotificationEvent (
348  IN  EFI_EVENT                           Event,
349  IN  VOID                                *Context
350  )
351{
352  EFI_STATUS                              Status;
353  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL      *FvbProtocol;
354  EFI_FAULT_TOLERANT_WRITE_PROTOCOL       *FtwProtocol;
355  EFI_PHYSICAL_ADDRESS                    NvStorageVariableBase;
356  EFI_GCD_MEMORY_SPACE_DESCRIPTOR         GcdDescriptor;
357  EFI_PHYSICAL_ADDRESS                    BaseAddress;
358  UINT64                                  Length;
359  EFI_PHYSICAL_ADDRESS                    VariableStoreBase;
360  UINT64                                  VariableStoreLength;
361  UINTN                                   FtwMaxBlockSize;
362
363  //
364  // Ensure FTW protocol is installed.
365  //
366  Status = GetFtwProtocol ((VOID**) &FtwProtocol);
367  if (EFI_ERROR (Status)) {
368    return ;
369  }
370
371  Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
372  if (!EFI_ERROR (Status)) {
373    ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize) <= FtwMaxBlockSize);
374  }
375
376  //
377  // Find the proper FVB protocol for variable.
378  //
379  NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
380  if (NvStorageVariableBase == 0) {
381    NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);
382  }
383  Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
384  if (EFI_ERROR (Status)) {
385    return ;
386  }
387  mVariableModuleGlobal->FvbInstance = FvbProtocol;
388
389  //
390  // Mark the variable storage region of the FLASH as RUNTIME.
391  //
392  VariableStoreBase   = NvStorageVariableBase + (((EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)(NvStorageVariableBase))->HeaderLength);
393  VariableStoreLength = ((VARIABLE_STORE_HEADER *)(UINTN)VariableStoreBase)->Size;
394  BaseAddress = VariableStoreBase & (~EFI_PAGE_MASK);
395  Length      = VariableStoreLength + (VariableStoreBase - BaseAddress);
396  Length      = (Length + EFI_PAGE_SIZE - 1) & (~EFI_PAGE_MASK);
397
398  Status      = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor);
399  if (EFI_ERROR (Status)) {
400    DEBUG ((DEBUG_WARN, "Variable driver failed to get flash memory attribute.\n"));
401  } else {
402    Status = gDS->SetMemorySpaceAttributes (
403                    BaseAddress,
404                    Length,
405                    GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME
406                    );
407    if (EFI_ERROR (Status)) {
408      DEBUG ((DEBUG_WARN, "Variable driver failed to add EFI_MEMORY_RUNTIME attribute to Flash.\n"));
409    }
410  }
411
412  Status = VariableWriteServiceInitialize ();
413  if (EFI_ERROR (Status)) {
414    DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status));
415  }
416
417  //
418  // Install the Variable Write Architectural protocol.
419  //
420  Status = gBS->InstallProtocolInterface (
421                  &mHandle,
422                  &gEfiVariableWriteArchProtocolGuid,
423                  EFI_NATIVE_INTERFACE,
424                  NULL
425                  );
426  ASSERT_EFI_ERROR (Status);
427
428  //
429  // Close the notify event to avoid install gEfiVariableWriteArchProtocolGuid again.
430  //
431  gBS->CloseEvent (Event);
432
433}
434
435
436/**
437  Variable Driver main entry point. The Variable driver places the 4 EFI
438  runtime services in the EFI System Table and installs arch protocols
439  for variable read and write services being available. It also registers
440  a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
441
442  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
443  @param[in] SystemTable    A pointer to the EFI System Table.
444
445  @retval EFI_SUCCESS       Variable service successfully initialized.
446
447**/
448EFI_STATUS
449EFIAPI
450VariableServiceInitialize (
451  IN EFI_HANDLE                         ImageHandle,
452  IN EFI_SYSTEM_TABLE                   *SystemTable
453  )
454{
455  EFI_STATUS                            Status;
456  EFI_EVENT                             ReadyToBootEvent;
457  EFI_EVENT                             EndOfDxeEvent;
458
459  Status = VariableCommonInitialize ();
460  ASSERT_EFI_ERROR (Status);
461
462  Status = gBS->InstallMultipleProtocolInterfaces (
463                  &mHandle,
464                  &gEdkiiVariableLockProtocolGuid,
465                  &mVariableLock,
466                  NULL
467                  );
468  ASSERT_EFI_ERROR (Status);
469
470  Status = gBS->InstallMultipleProtocolInterfaces (
471                  &mHandle,
472                  &gEdkiiVarCheckProtocolGuid,
473                  &mVarCheck,
474                  NULL
475                  );
476  ASSERT_EFI_ERROR (Status);
477
478  SystemTable->RuntimeServices->GetVariable         = VariableServiceGetVariable;
479  SystemTable->RuntimeServices->GetNextVariableName = VariableServiceGetNextVariableName;
480  SystemTable->RuntimeServices->SetVariable         = VariableServiceSetVariable;
481  SystemTable->RuntimeServices->QueryVariableInfo   = VariableServiceQueryVariableInfo;
482
483  //
484  // Now install the Variable Runtime Architectural protocol on a new handle.
485  //
486  Status = gBS->InstallProtocolInterface (
487                  &mHandle,
488                  &gEfiVariableArchProtocolGuid,
489                  EFI_NATIVE_INTERFACE,
490                  NULL
491                  );
492  ASSERT_EFI_ERROR (Status);
493
494  //
495  // Register FtwNotificationEvent () notify function.
496  //
497  EfiCreateProtocolNotifyEvent (
498    &gEfiFaultTolerantWriteProtocolGuid,
499    TPL_CALLBACK,
500    FtwNotificationEvent,
501    (VOID *)SystemTable,
502    &mFtwRegistration
503    );
504
505  Status = gBS->CreateEventEx (
506                  EVT_NOTIFY_SIGNAL,
507                  TPL_NOTIFY,
508                  VariableClassAddressChangeEvent,
509                  NULL,
510                  &gEfiEventVirtualAddressChangeGuid,
511                  &mVirtualAddressChangeEvent
512                  );
513  ASSERT_EFI_ERROR (Status);
514
515  //
516  // Register the event handling function to reclaim variable for OS usage.
517  //
518  Status = EfiCreateEventReadyToBootEx (
519             TPL_NOTIFY,
520             OnReadyToBoot,
521             NULL,
522             &ReadyToBootEvent
523             );
524  ASSERT_EFI_ERROR (Status);
525
526  //
527  // Register the event handling function to set the End Of DXE flag.
528  //
529  Status = gBS->CreateEventEx (
530                  EVT_NOTIFY_SIGNAL,
531                  TPL_NOTIFY,
532                  OnEndOfDxe,
533                  NULL,
534                  &gEfiEndOfDxeEventGroupGuid,
535                  &EndOfDxeEvent
536                  );
537  ASSERT_EFI_ERROR (Status);
538
539  return EFI_SUCCESS;
540}
541
542