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