VariableSmm.c revision 9d00d20ed40fb56d8b5a8e1a3f7ae3e491ceaf94
1/** @file
2
3  The sample implementation for SMM variable protocol. And this driver
4  implements  an SMI handler to communicate with the DXE runtime driver
5  to provide variable services.
6
7  Caution: This module requires additional review when modified.
8  This driver will have external input - variable data and communicate buffer in SMM mode.
9  This external input must be validated carefully to avoid security issue like
10  buffer overflow, integer overflow.
11
12  SmmVariableHandler() will receive untrusted input and do basic validation.
13
14  Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
15  VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
16  SmmVariableGetStatistics() should also do validation based on its own knowledge.
17
18Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.<BR>
19This program and the accompanying materials
20are licensed and made available under the terms and conditions of the BSD License
21which accompanies this distribution.  The full text of the license may be found at
22http://opensource.org/licenses/bsd-license.php
23
24THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
25WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
26
27**/
28#include <Protocol/SmmVariable.h>
29#include <Protocol/SmmFirmwareVolumeBlock.h>
30#include <Protocol/SmmFaultTolerantWrite.h>
31#include <Protocol/SmmAccess2.h>
32
33#include <Library/SmmServicesTableLib.h>
34
35#include <Guid/VariableFormat.h>
36#include <Guid/SmmVariableCommon.h>
37#include "Variable.h"
38
39EFI_SMRAM_DESCRIPTOR                                 *mSmramRanges;
40UINTN                                                mSmramRangeCount;
41
42extern VARIABLE_INFO_ENTRY                           *gVariableInfo;
43EFI_HANDLE                                           mSmmVariableHandle      = NULL;
44EFI_HANDLE                                           mVariableHandle         = NULL;
45BOOLEAN                                              mAtRuntime              = FALSE;
46EFI_GUID                                             mZeroGuid               = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
47
48EFI_SMM_VARIABLE_PROTOCOL      gSmmVariable = {
49  VariableServiceGetVariable,
50  VariableServiceGetNextVariableName,
51  VariableServiceSetVariable,
52  VariableServiceQueryVariableInfo
53};
54
55
56/**
57  Return TRUE if ExitBootServices () has been called.
58
59  @retval TRUE If ExitBootServices () has been called.
60**/
61BOOLEAN
62AtRuntime (
63  VOID
64  )
65{
66  return mAtRuntime;
67}
68
69/**
70  This function check if the address is in SMRAM.
71
72  @param Buffer  the buffer address to be checked.
73  @param Length  the buffer length to be checked.
74
75  @retval TRUE  this address is in SMRAM.
76  @retval FALSE this address is NOT in SMRAM.
77**/
78BOOLEAN
79InternalIsAddressInSmram (
80  IN EFI_PHYSICAL_ADDRESS  Buffer,
81  IN UINT64                Length
82  )
83{
84  UINTN  Index;
85
86  for (Index = 0; Index < mSmramRangeCount; Index ++) {
87    if (((Buffer >= mSmramRanges[Index].CpuStart) && (Buffer < mSmramRanges[Index].CpuStart + mSmramRanges[Index].PhysicalSize)) ||
88        ((mSmramRanges[Index].CpuStart >= Buffer) && (mSmramRanges[Index].CpuStart < Buffer + Length))) {
89      return TRUE;
90    }
91  }
92
93  return FALSE;
94}
95
96/**
97  This function check if the address refered by Buffer and Length is valid.
98
99  @param Buffer  the buffer address to be checked.
100  @param Length  the buffer length to be checked.
101
102  @retval TRUE  this address is valid.
103  @retval FALSE this address is NOT valid.
104**/
105BOOLEAN
106InternalIsAddressValid (
107  IN UINTN                 Buffer,
108  IN UINTN                 Length
109  )
110{
111  if (Buffer > (MAX_ADDRESS - Length)) {
112    //
113    // Overflow happen
114    //
115    return FALSE;
116  }
117  if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)Buffer, (UINT64)Length)) {
118    return FALSE;
119  }
120  return TRUE;
121}
122
123/**
124  Initializes a basic mutual exclusion lock.
125
126  This function initializes a basic mutual exclusion lock to the released state
127  and returns the lock.  Each lock provides mutual exclusion access at its task
128  priority level.  Since there is no preemption or multiprocessor support in EFI,
129  acquiring the lock only consists of raising to the locks TPL.
130  If Lock is NULL, then ASSERT().
131  If Priority is not a valid TPL value, then ASSERT().
132
133  @param  Lock       A pointer to the lock data structure to initialize.
134  @param  Priority   EFI TPL is associated with the lock.
135
136  @return The lock.
137
138**/
139EFI_LOCK *
140InitializeLock (
141  IN OUT EFI_LOCK                         *Lock,
142  IN EFI_TPL                              Priority
143  )
144{
145  return Lock;
146}
147
148/**
149  Acquires lock only at boot time. Simply returns at runtime.
150
151  This is a temperary function that will be removed when
152  EfiAcquireLock() in UefiLib can handle the call in UEFI
153  Runtimer driver in RT phase.
154  It calls EfiAcquireLock() at boot time, and simply returns
155  at runtime.
156
157  @param  Lock         A pointer to the lock to acquire.
158
159**/
160VOID
161AcquireLockOnlyAtBootTime (
162  IN EFI_LOCK                             *Lock
163  )
164{
165
166}
167
168
169/**
170  Releases lock only at boot time. Simply returns at runtime.
171
172  This is a temperary function which will be removed when
173  EfiReleaseLock() in UefiLib can handle the call in UEFI
174  Runtimer driver in RT phase.
175  It calls EfiReleaseLock() at boot time and simply returns
176  at runtime.
177
178  @param  Lock         A pointer to the lock to release.
179
180**/
181VOID
182ReleaseLockOnlyAtBootTime (
183  IN EFI_LOCK                             *Lock
184  )
185{
186
187}
188
189/**
190  Retrive the SMM Fault Tolerent Write protocol interface.
191
192  @param[out] FtwProtocol       The interface of SMM Ftw protocol
193
194  @retval EFI_SUCCESS           The SMM FTW protocol instance was found and returned in FtwProtocol.
195  @retval EFI_NOT_FOUND         The SMM FTW protocol instance was not found.
196  @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
197
198**/
199EFI_STATUS
200GetFtwProtocol (
201  OUT VOID                                **FtwProtocol
202  )
203{
204  EFI_STATUS                              Status;
205
206  //
207  // Locate Smm Fault Tolerent Write protocol
208  //
209  Status = gSmst->SmmLocateProtocol (
210                    &gEfiSmmFaultTolerantWriteProtocolGuid,
211                    NULL,
212                    FtwProtocol
213                    );
214  return Status;
215}
216
217
218/**
219  Retrive the SMM FVB protocol interface by HANDLE.
220
221  @param[in]  FvBlockHandle     The handle of SMM FVB protocol that provides services for
222                                reading, writing, and erasing the target block.
223  @param[out] FvBlock           The interface of SMM FVB protocol
224
225  @retval EFI_SUCCESS           The interface information for the specified protocol was returned.
226  @retval EFI_UNSUPPORTED       The device does not support the SMM FVB protocol.
227  @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
228
229**/
230EFI_STATUS
231GetFvbByHandle (
232  IN  EFI_HANDLE                          FvBlockHandle,
233  OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock
234  )
235{
236  //
237  // To get the SMM FVB protocol interface on the handle
238  //
239  return gSmst->SmmHandleProtocol (
240                  FvBlockHandle,
241                  &gEfiSmmFirmwareVolumeBlockProtocolGuid,
242                  (VOID **) FvBlock
243                  );
244}
245
246
247/**
248  Function returns an array of handles that support the SMM FVB protocol
249  in a buffer allocated from pool.
250
251  @param[out]  NumberHandles    The number of handles returned in Buffer.
252  @param[out]  Buffer           A pointer to the buffer to return the requested
253                                array of  handles that support SMM FVB protocol.
254
255  @retval EFI_SUCCESS           The array of handles was returned in Buffer, and the number of
256                                handles in Buffer was returned in NumberHandles.
257  @retval EFI_NOT_FOUND         No SMM FVB handle was found.
258  @retval EFI_OUT_OF_RESOURCES  There is not enough pool memory to store the matching results.
259  @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
260
261**/
262EFI_STATUS
263GetFvbCountAndBuffer (
264  OUT UINTN                               *NumberHandles,
265  OUT EFI_HANDLE                          **Buffer
266  )
267{
268  EFI_STATUS                              Status;
269  UINTN                                   BufferSize;
270
271  if ((NumberHandles == NULL) || (Buffer == NULL)) {
272    return EFI_INVALID_PARAMETER;
273  }
274
275  BufferSize     = 0;
276  *NumberHandles = 0;
277  *Buffer        = NULL;
278  Status = gSmst->SmmLocateHandle (
279                    ByProtocol,
280                    &gEfiSmmFirmwareVolumeBlockProtocolGuid,
281                    NULL,
282                    &BufferSize,
283                    *Buffer
284                    );
285  if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
286    return EFI_NOT_FOUND;
287  }
288
289  *Buffer = AllocatePool (BufferSize);
290  if (*Buffer == NULL) {
291    return EFI_OUT_OF_RESOURCES;
292  }
293
294  Status = gSmst->SmmLocateHandle (
295                    ByProtocol,
296                    &gEfiSmmFirmwareVolumeBlockProtocolGuid,
297                    NULL,
298                    &BufferSize,
299                    *Buffer
300                    );
301
302  *NumberHandles = BufferSize / sizeof(EFI_HANDLE);
303  if (EFI_ERROR(Status)) {
304    *NumberHandles = 0;
305  }
306
307  return Status;
308}
309
310
311/**
312  Get the variable statistics information from the information buffer pointed by gVariableInfo.
313
314  Caution: This function may be invoked at SMM runtime.
315  InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
316
317  @param[in, out]  InfoEntry    A pointer to the buffer of variable information entry.
318                                On input, point to the variable information returned last time. if
319                                InfoEntry->VendorGuid is zero, return the first information.
320                                On output, point to the next variable information.
321  @param[in, out]  InfoSize     On input, the size of the variable information buffer.
322                                On output, the returned variable information size.
323
324  @retval EFI_SUCCESS          The variable information is found and returned successfully.
325  @retval EFI_UNSUPPORTED      No variable inoformation exists in variable driver. The
326                               PcdVariableCollectStatistics should be set TRUE to support it.
327  @retval EFI_BUFFER_TOO_SMALL The buffer is too small to hold the next variable information.
328
329**/
330EFI_STATUS
331SmmVariableGetStatistics (
332  IN OUT VARIABLE_INFO_ENTRY                           *InfoEntry,
333  IN OUT UINTN                                         *InfoSize
334  )
335{
336  VARIABLE_INFO_ENTRY                                  *VariableInfo;
337  UINTN                                                NameLength;
338  UINTN                                                StatisticsInfoSize;
339  CHAR16                                               *InfoName;
340
341  ASSERT (InfoEntry != NULL);
342  VariableInfo = gVariableInfo;
343  if (VariableInfo == NULL) {
344    return EFI_UNSUPPORTED;
345  }
346
347  StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
348  if (*InfoSize < StatisticsInfoSize) {
349    *InfoSize = StatisticsInfoSize;
350    return EFI_BUFFER_TOO_SMALL;
351  }
352  InfoName = (CHAR16 *)(InfoEntry + 1);
353
354  if (CompareGuid (&InfoEntry->VendorGuid, &mZeroGuid)) {
355    //
356    // Return the first variable info
357    //
358    CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
359    CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
360    *InfoSize = StatisticsInfoSize;
361    return EFI_SUCCESS;
362  }
363
364  //
365  // Get the next variable info
366  //
367  while (VariableInfo != NULL) {
368    if (CompareGuid (&VariableInfo->VendorGuid, &InfoEntry->VendorGuid)) {
369      NameLength = StrSize (VariableInfo->Name);
370      if (NameLength == StrSize (InfoName)) {
371        if (CompareMem (VariableInfo->Name, InfoName, NameLength) == 0) {
372          //
373          // Find the match one
374          //
375          VariableInfo = VariableInfo->Next;
376          break;
377        }
378      }
379    }
380    VariableInfo = VariableInfo->Next;
381  };
382
383  if (VariableInfo == NULL) {
384    *InfoSize = 0;
385    return EFI_SUCCESS;
386  }
387
388  //
389  // Output the new variable info
390  //
391  StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + StrSize (VariableInfo->Name);
392  if (*InfoSize < StatisticsInfoSize) {
393    *InfoSize = StatisticsInfoSize;
394    return EFI_BUFFER_TOO_SMALL;
395  }
396
397  CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
398  CopyMem (InfoName, VariableInfo->Name, StrSize (VariableInfo->Name));
399  *InfoSize = StatisticsInfoSize;
400
401  return EFI_SUCCESS;
402}
403
404
405/**
406  Communication service SMI Handler entry.
407
408  This SMI handler provides services for the variable wrapper driver.
409
410  Caution: This function may receive untrusted input.
411  This variable data and communicate buffer are external input, so this function will do basic validation.
412  Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
413  VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
414  SmmVariableGetStatistics() should also do validation based on its own knowledge.
415
416  @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
417  @param[in]     RegisterContext Points to an optional handler context which was specified when the
418                                 handler was registered.
419  @param[in, out] CommBuffer     A pointer to a collection of data in memory that will
420                                 be conveyed from a non-SMM environment into an SMM environment.
421  @param[in, out] CommBufferSize The size of the CommBuffer.
422
423  @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers
424                                              should still be called.
425  @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should
426                                              still be called.
427  @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still
428                                              be called.
429  @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
430**/
431EFI_STATUS
432EFIAPI
433SmmVariableHandler (
434  IN     EFI_HANDLE                                DispatchHandle,
435  IN     CONST VOID                                *RegisterContext,
436  IN OUT VOID                                      *CommBuffer,
437  IN OUT UINTN                                     *CommBufferSize
438  )
439{
440  EFI_STATUS                                       Status;
441  SMM_VARIABLE_COMMUNICATE_HEADER                  *SmmVariableFunctionHeader;
442  SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE         *SmmVariableHeader;
443  SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME  *GetNextVariableName;
444  SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO     *QueryVariableInfo;
445  VARIABLE_INFO_ENTRY                              *VariableInfo;
446  UINTN                                            InfoSize;
447  UINTN                                            NameBufferSize;
448
449  //
450  // If input is invalid, stop processing this SMI
451  //
452  if (CommBuffer == NULL || CommBufferSize == NULL) {
453    return EFI_SUCCESS;
454  }
455
456  if (*CommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
457    return EFI_SUCCESS;
458  }
459
460  if (!InternalIsAddressValid ((UINTN)CommBuffer, *CommBufferSize)) {
461    DEBUG ((EFI_D_ERROR, "SMM communication buffer in SMRAM or overflow!\n"));
462    return EFI_SUCCESS;
463  }
464
465  SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
466  switch (SmmVariableFunctionHeader->Function) {
467    case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
468      SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;
469      if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
470         ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
471        //
472        // Prevent InfoSize overflow happen
473        //
474        Status = EFI_ACCESS_DENIED;
475        goto EXIT;
476      }
477      InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
478                 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
479
480      //
481      // SMRAM range check already covered before
482      //
483      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
484        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
485        Status = EFI_ACCESS_DENIED;
486        goto EXIT;
487      }
488
489      if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
490        //
491        // Make sure VariableName is A Null-terminated string.
492        //
493        Status = EFI_ACCESS_DENIED;
494        goto EXIT;
495      }
496
497      Status = VariableServiceGetVariable (
498                 SmmVariableHeader->Name,
499                 &SmmVariableHeader->Guid,
500                 &SmmVariableHeader->Attributes,
501                 &SmmVariableHeader->DataSize,
502                 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
503                 );
504      break;
505
506    case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
507      GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) SmmVariableFunctionHeader->Data;
508      if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
509        //
510        // Prevent InfoSize overflow happen
511        //
512        Status = EFI_ACCESS_DENIED;
513        goto EXIT;
514      }
515      InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;
516
517      //
518      // SMRAM range check already covered before
519      //
520      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
521        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
522        Status = EFI_ACCESS_DENIED;
523        goto EXIT;
524      }
525
526      NameBufferSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE -  OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
527      if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {
528        //
529        // Make sure input VariableName is A Null-terminated string.
530        //
531        Status = EFI_ACCESS_DENIED;
532        goto EXIT;
533      }
534
535      Status = VariableServiceGetNextVariableName (
536                 &GetNextVariableName->NameSize,
537                 GetNextVariableName->Name,
538                 &GetNextVariableName->Guid
539                 );
540      break;
541
542    case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
543      SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) SmmVariableFunctionHeader->Data;
544      if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
545         ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
546        //
547        // Prevent InfoSize overflow happen
548        //
549        Status = EFI_ACCESS_DENIED;
550        goto EXIT;
551      }
552      InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
553                 + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
554
555      //
556      // SMRAM range check already covered before
557      // Data buffer should not contain SMM range
558      //
559      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
560        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
561        Status = EFI_ACCESS_DENIED;
562        goto EXIT;
563      }
564
565      if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
566        //
567        // Make sure VariableName is A Null-terminated string.
568        //
569        Status = EFI_ACCESS_DENIED;
570        goto EXIT;
571      }
572
573      Status = VariableServiceSetVariable (
574                 SmmVariableHeader->Name,
575                 &SmmVariableHeader->Guid,
576                 SmmVariableHeader->Attributes,
577                 SmmVariableHeader->DataSize,
578                 (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
579                 );
580      break;
581
582    case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
583      QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
584      InfoSize = sizeof(SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO);
585
586      //
587      // SMRAM range check already covered before
588      //
589      if (InfoSize > *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
590        DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));
591        Status = EFI_ACCESS_DENIED;
592        goto EXIT;
593      }
594
595      Status = VariableServiceQueryVariableInfo (
596                 QueryVariableInfo->Attributes,
597                 &QueryVariableInfo->MaximumVariableStorageSize,
598                 &QueryVariableInfo->RemainingVariableStorageSize,
599                 &QueryVariableInfo->MaximumVariableSize
600                 );
601      break;
602
603    case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
604      if (AtRuntime()) {
605        Status = EFI_UNSUPPORTED;
606        break;
607      }
608      ReclaimForOS ();
609      Status = EFI_SUCCESS;
610      break;
611
612    case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:
613      mAtRuntime = TRUE;
614      Status = EFI_SUCCESS;
615      break;
616
617    case SMM_VARIABLE_FUNCTION_GET_STATISTICS:
618      VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;
619      InfoSize = *CommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
620
621      //
622      // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
623      // It is covered by previous CommBuffer check
624      //
625
626      if (InternalIsAddressInSmram ((EFI_PHYSICAL_ADDRESS)(UINTN)CommBufferSize, sizeof(UINTN))) {
627        DEBUG ((EFI_D_ERROR, "SMM communication buffer in SMRAM!\n"));
628        Status = EFI_ACCESS_DENIED;
629        goto EXIT;
630      }
631
632      Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);
633      *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
634      break;
635
636    default:
637      Status = EFI_UNSUPPORTED;
638  }
639
640EXIT:
641
642  SmmVariableFunctionHeader->ReturnStatus = Status;
643
644  return EFI_SUCCESS;
645}
646
647
648/**
649  SMM Fault Tolerant Write protocol notification event handler.
650
651  Non-Volatile variable write may needs FTW protocol to reclaim when
652  writting variable.
653
654  @param  Protocol   Points to the protocol's unique identifier
655  @param  Interface  Points to the interface instance
656  @param  Handle     The handle on which the interface was installed
657
658  @retval EFI_SUCCESS   SmmEventCallback runs successfully
659  @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
660
661 **/
662EFI_STATUS
663EFIAPI
664SmmFtwNotificationEvent (
665  IN CONST EFI_GUID                       *Protocol,
666  IN VOID                                 *Interface,
667  IN EFI_HANDLE                           Handle
668  )
669{
670  EFI_STATUS                              Status;
671  EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvbProtocol;
672  EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL   *FtwProtocol;
673  EFI_PHYSICAL_ADDRESS                    NvStorageVariableBase;
674
675  if (mVariableModuleGlobal->FvbInstance != NULL) {
676    return EFI_SUCCESS;
677  }
678
679  //
680  // Ensure SMM FTW protocol is installed.
681  //
682  Status = GetFtwProtocol ((VOID **)&FtwProtocol);
683  if (EFI_ERROR (Status)) {
684    return Status;
685  }
686
687  //
688  // Find the proper FVB protocol for variable.
689  //
690  NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageVariableBase64);
691  if (NvStorageVariableBase == 0) {
692    NvStorageVariableBase = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageVariableBase);
693  }
694  Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
695  if (EFI_ERROR (Status)) {
696    return EFI_NOT_FOUND;
697  }
698
699  mVariableModuleGlobal->FvbInstance = FvbProtocol;
700
701  Status = VariableWriteServiceInitialize ();
702  ASSERT_EFI_ERROR (Status);
703
704  //
705  // Notify the variable wrapper driver the variable write service is ready
706  //
707  Status = gBS->InstallProtocolInterface (
708                  &mSmmVariableHandle,
709                  &gSmmVariableWriteGuid,
710                  EFI_NATIVE_INTERFACE,
711                  NULL
712                  );
713  ASSERT_EFI_ERROR (Status);
714
715  return EFI_SUCCESS;
716}
717
718
719/**
720  Variable Driver main entry point. The Variable driver places the 4 EFI
721  runtime services in the EFI System Table and installs arch protocols
722  for variable read and write services being available. It also registers
723  a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
724
725  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
726  @param[in] SystemTable    A pointer to the EFI System Table.
727
728  @retval EFI_SUCCESS       Variable service successfully initialized.
729
730**/
731EFI_STATUS
732EFIAPI
733VariableServiceInitialize (
734  IN EFI_HANDLE                           ImageHandle,
735  IN EFI_SYSTEM_TABLE                     *SystemTable
736  )
737{
738  EFI_STATUS                              Status;
739  EFI_HANDLE                              VariableHandle;
740  VOID                                    *SmmFtwRegistration;
741  EFI_SMM_ACCESS2_PROTOCOL                *SmmAccess;
742  UINTN                                   Size;
743
744  //
745  // Variable initialize.
746  //
747  Status = VariableCommonInitialize ();
748  ASSERT_EFI_ERROR (Status);
749
750  //
751  // Install the Smm Variable Protocol on a new handle.
752  //
753  VariableHandle = NULL;
754  Status = gSmst->SmmInstallProtocolInterface (
755                    &VariableHandle,
756                    &gEfiSmmVariableProtocolGuid,
757                    EFI_NATIVE_INTERFACE,
758                    &gSmmVariable
759                    );
760  ASSERT_EFI_ERROR (Status);
761
762  //
763  // Get SMRAM information
764  //
765  Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess);
766  ASSERT_EFI_ERROR (Status);
767
768  Size = 0;
769  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL);
770  ASSERT (Status == EFI_BUFFER_TOO_SMALL);
771
772  Status = gSmst->SmmAllocatePool (
773                    EfiRuntimeServicesData,
774                    Size,
775                    (VOID **)&mSmramRanges
776                    );
777  ASSERT_EFI_ERROR (Status);
778
779  Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmramRanges);
780  ASSERT_EFI_ERROR (Status);
781
782  mSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR);
783
784  ///
785  /// Register SMM variable SMI handler
786  ///
787  VariableHandle = NULL;
788  Status = gSmst->SmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
789  ASSERT_EFI_ERROR (Status);
790
791  //
792  // Notify the variable wrapper driver the variable service is ready
793  //
794  Status = SystemTable->BootServices->InstallProtocolInterface (
795                                        &mVariableHandle,
796                                        &gEfiSmmVariableProtocolGuid,
797                                        EFI_NATIVE_INTERFACE,
798                                        &gSmmVariable
799                                        );
800  ASSERT_EFI_ERROR (Status);
801
802  //
803  // Register FtwNotificationEvent () notify function.
804  //
805  Status = gSmst->SmmRegisterProtocolNotify (
806                    &gEfiSmmFaultTolerantWriteProtocolGuid,
807                    SmmFtwNotificationEvent,
808                    &SmmFtwRegistration
809                    );
810  ASSERT_EFI_ERROR (Status);
811
812  SmmFtwNotificationEvent (NULL, NULL, NULL);
813
814  return EFI_SUCCESS;
815}
816
817
818