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