• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
18 This program and the accompanying materials
19 are licensed and made available under the terms and conditions of the BSD License
20 which accompanies this distribution.  The full text of the license may be found at
21 http://opensource.org/licenses/bsd-license.php
22 
23 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
24 WITHOUT 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 
41 extern VARIABLE_INFO_ENTRY                           *gVariableInfo;
42 EFI_HANDLE                                           mSmmVariableHandle      = NULL;
43 EFI_HANDLE                                           mVariableHandle         = NULL;
44 BOOLEAN                                              mAtRuntime              = FALSE;
45 UINT8                                                *mVariableBufferPayload = NULL;
46 UINTN                                                mVariableBufferPayloadSize;
47 extern BOOLEAN                                       mEndOfDxe;
48 extern 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 **/
57 VOID
58 EFIAPI
SecureBootHook(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid)59 SecureBootHook (
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 **/
85 EFI_STATUS
86 EFIAPI
SmmVariableSetVariable(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)87 SmmVariableSetVariable (
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 
112 EFI_SMM_VARIABLE_PROTOCOL      gSmmVariable = {
113   VariableServiceGetVariable,
114   VariableServiceGetNextVariableName,
115   SmmVariableSetVariable,
116   VariableServiceQueryVariableInfo
117 };
118 
119 EDKII_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 **/
128 BOOLEAN
AtRuntime(VOID)129 AtRuntime (
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 **/
152 EFI_LOCK *
InitializeLock(IN OUT EFI_LOCK * Lock,IN EFI_TPL Priority)153 InitializeLock (
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 **/
173 VOID
AcquireLockOnlyAtBootTime(IN EFI_LOCK * Lock)174 AcquireLockOnlyAtBootTime (
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 **/
194 VOID
ReleaseLockOnlyAtBootTime(IN EFI_LOCK * Lock)195 ReleaseLockOnlyAtBootTime (
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 **/
212 EFI_STATUS
GetFtwProtocol(OUT VOID ** FtwProtocol)213 GetFtwProtocol (
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 **/
243 EFI_STATUS
GetFvbByHandle(IN EFI_HANDLE FvBlockHandle,OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL ** FvBlock)244 GetFvbByHandle (
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 **/
275 EFI_STATUS
GetFvbCountAndBuffer(OUT UINTN * NumberHandles,OUT EFI_HANDLE ** Buffer)276 GetFvbCountAndBuffer (
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 **/
346 EFI_STATUS
SmmVariableGetStatistics(IN OUT VARIABLE_INFO_ENTRY * InfoEntry,IN OUT UINTN * InfoSize)347 SmmVariableGetStatistics (
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 **/
453 EFI_STATUS
454 EFIAPI
SmmVariableHandler(IN EFI_HANDLE DispatchHandle,IN CONST VOID * RegisterContext,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)455 SmmVariableHandler (
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 
781 EXIT:
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 **/
798 EFI_STATUS
799 EFIAPI
SmmEndOfDxeCallback(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)800 SmmEndOfDxeCallback (
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  **/
834 EFI_STATUS
835 EFIAPI
SmmFtwNotificationEvent(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)836 SmmFtwNotificationEvent (
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 **/
911 EFI_STATUS
912 EFIAPI
VariableServiceInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)913 VariableServiceInitialize (
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