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