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 - 2019, Intel Corporation. All rights reserved.<BR>
18 Copyright (c) 2018, Linaro, Ltd. All rights reserved.<BR>
19 SPDX-License-Identifier: BSD-2-Clause-Patent
20 
21 **/
22 
23 #include <Protocol/SmmVariable.h>
24 #include <Protocol/SmmFirmwareVolumeBlock.h>
25 #include <Protocol/SmmFaultTolerantWrite.h>
26 #include <Protocol/MmEndOfDxe.h>
27 #include <Protocol/SmmVarCheck.h>
28 
29 #include <Library/MmServicesTableLib.h>
30 
31 #include <Guid/SmmVariableCommon.h>
32 #include "Variable.h"
33 #include "VariableParsing.h"
34 #include "VariableRuntimeCache.h"
35 
36 extern VARIABLE_STORE_HEADER                         *mNvVariableCache;
37 
38 BOOLEAN                                              mAtRuntime              = FALSE;
39 UINT8                                                *mVariableBufferPayload = NULL;
40 UINTN                                                mVariableBufferPayloadSize;
41 
42 /**
43   SecureBoot Hook for SetVariable.
44 
45   @param[in] VariableName                 Name of Variable to be found.
46   @param[in] VendorGuid                   Variable vendor GUID.
47 
48 **/
49 VOID
50 EFIAPI
SecureBootHook(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid)51 SecureBootHook (
52   IN CHAR16                                 *VariableName,
53   IN EFI_GUID                               *VendorGuid
54   )
55 {
56   return ;
57 }
58 
59 /**
60 
61   This code sets variable in storage blocks (Volatile or Non-Volatile).
62 
63   @param VariableName                     Name of Variable to be found.
64   @param VendorGuid                       Variable vendor GUID.
65   @param Attributes                       Attribute value of the variable found
66   @param DataSize                         Size of Data found. If size is less than the
67                                           data, this value contains the required size.
68   @param Data                             Data pointer.
69 
70   @return EFI_INVALID_PARAMETER           Invalid parameter.
71   @return EFI_SUCCESS                     Set successfully.
72   @return EFI_OUT_OF_RESOURCES            Resource not enough to set variable.
73   @return EFI_NOT_FOUND                   Not found.
74   @return EFI_WRITE_PROTECTED             Variable is read-only.
75 
76 **/
77 EFI_STATUS
78 EFIAPI
SmmVariableSetVariable(IN CHAR16 * VariableName,IN EFI_GUID * VendorGuid,IN UINT32 Attributes,IN UINTN DataSize,IN VOID * Data)79 SmmVariableSetVariable (
80   IN CHAR16                  *VariableName,
81   IN EFI_GUID                *VendorGuid,
82   IN UINT32                  Attributes,
83   IN UINTN                   DataSize,
84   IN VOID                    *Data
85   )
86 {
87   EFI_STATUS                 Status;
88 
89   //
90   // Disable write protection when the calling SetVariable() through EFI_SMM_VARIABLE_PROTOCOL.
91   //
92   mRequestSource = VarCheckFromTrusted;
93   Status         = VariableServiceSetVariable (
94                      VariableName,
95                      VendorGuid,
96                      Attributes,
97                      DataSize,
98                      Data
99                      );
100   mRequestSource = VarCheckFromUntrusted;
101   return Status;
102 }
103 
104 EFI_SMM_VARIABLE_PROTOCOL      gSmmVariable = {
105   VariableServiceGetVariable,
106   VariableServiceGetNextVariableName,
107   SmmVariableSetVariable,
108   VariableServiceQueryVariableInfo
109 };
110 
111 EDKII_SMM_VAR_CHECK_PROTOCOL mSmmVarCheck = { VarCheckRegisterSetVariableCheckHandler,
112                                               VarCheckVariablePropertySet,
113                                               VarCheckVariablePropertyGet };
114 
115 /**
116   Return TRUE if ExitBootServices () has been called.
117 
118   @retval TRUE If ExitBootServices () has been called.
119 **/
120 BOOLEAN
AtRuntime(VOID)121 AtRuntime (
122   VOID
123   )
124 {
125   return mAtRuntime;
126 }
127 
128 /**
129   Initializes a basic mutual exclusion lock.
130 
131   This function initializes a basic mutual exclusion lock to the released state
132   and returns the lock.  Each lock provides mutual exclusion access at its task
133   priority level.  Since there is no preemption or multiprocessor support in EFI,
134   acquiring the lock only consists of raising to the locks TPL.
135   If Lock is NULL, then ASSERT().
136   If Priority is not a valid TPL value, then ASSERT().
137 
138   @param  Lock       A pointer to the lock data structure to initialize.
139   @param  Priority   EFI TPL is associated with the lock.
140 
141   @return The lock.
142 
143 **/
144 EFI_LOCK *
InitializeLock(IN OUT EFI_LOCK * Lock,IN EFI_TPL Priority)145 InitializeLock (
146   IN OUT EFI_LOCK                         *Lock,
147   IN EFI_TPL                              Priority
148   )
149 {
150   return Lock;
151 }
152 
153 /**
154   Acquires lock only at boot time. Simply returns at runtime.
155 
156   This is a temperary function that will be removed when
157   EfiAcquireLock() in UefiLib can handle the call in UEFI
158   Runtimer driver in RT phase.
159   It calls EfiAcquireLock() at boot time, and simply returns
160   at runtime.
161 
162   @param  Lock         A pointer to the lock to acquire.
163 
164 **/
165 VOID
AcquireLockOnlyAtBootTime(IN EFI_LOCK * Lock)166 AcquireLockOnlyAtBootTime (
167   IN EFI_LOCK                             *Lock
168   )
169 {
170 
171 }
172 
173 
174 /**
175   Releases lock only at boot time. Simply returns at runtime.
176 
177   This is a temperary function which will be removed when
178   EfiReleaseLock() in UefiLib can handle the call in UEFI
179   Runtimer driver in RT phase.
180   It calls EfiReleaseLock() at boot time and simply returns
181   at runtime.
182 
183   @param  Lock         A pointer to the lock to release.
184 
185 **/
186 VOID
ReleaseLockOnlyAtBootTime(IN EFI_LOCK * Lock)187 ReleaseLockOnlyAtBootTime (
188   IN EFI_LOCK                             *Lock
189   )
190 {
191 
192 }
193 
194 /**
195   Retrieve the SMM Fault Tolerent Write protocol interface.
196 
197   @param[out] FtwProtocol       The interface of SMM Ftw protocol
198 
199   @retval EFI_SUCCESS           The SMM FTW protocol instance was found and returned in FtwProtocol.
200   @retval EFI_NOT_FOUND         The SMM FTW protocol instance was not found.
201   @retval EFI_INVALID_PARAMETER SarProtocol is NULL.
202 
203 **/
204 EFI_STATUS
GetFtwProtocol(OUT VOID ** FtwProtocol)205 GetFtwProtocol (
206   OUT VOID                                **FtwProtocol
207   )
208 {
209   EFI_STATUS                              Status;
210 
211   //
212   // Locate Smm Fault Tolerent Write protocol
213   //
214   Status = gMmst->MmLocateProtocol (
215                     &gEfiSmmFaultTolerantWriteProtocolGuid,
216                     NULL,
217                     FtwProtocol
218                     );
219   return Status;
220 }
221 
222 
223 /**
224   Retrieve the SMM FVB protocol interface by HANDLE.
225 
226   @param[in]  FvBlockHandle     The handle of SMM FVB protocol that provides services for
227                                 reading, writing, and erasing the target block.
228   @param[out] FvBlock           The interface of SMM FVB protocol
229 
230   @retval EFI_SUCCESS           The interface information for the specified protocol was returned.
231   @retval EFI_UNSUPPORTED       The device does not support the SMM FVB protocol.
232   @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.
233 
234 **/
235 EFI_STATUS
GetFvbByHandle(IN EFI_HANDLE FvBlockHandle,OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL ** FvBlock)236 GetFvbByHandle (
237   IN  EFI_HANDLE                          FvBlockHandle,
238   OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock
239   )
240 {
241   //
242   // To get the SMM FVB protocol interface on the handle
243   //
244   return gMmst->MmHandleProtocol (
245                   FvBlockHandle,
246                   &gEfiSmmFirmwareVolumeBlockProtocolGuid,
247                   (VOID **) FvBlock
248                   );
249 }
250 
251 
252 /**
253   Function returns an array of handles that support the SMM FVB protocol
254   in a buffer allocated from pool.
255 
256   @param[out]  NumberHandles    The number of handles returned in Buffer.
257   @param[out]  Buffer           A pointer to the buffer to return the requested
258                                 array of  handles that support SMM FVB protocol.
259 
260   @retval EFI_SUCCESS           The array of handles was returned in Buffer, and the number of
261                                 handles in Buffer was returned in NumberHandles.
262   @retval EFI_NOT_FOUND         No SMM FVB handle was found.
263   @retval EFI_OUT_OF_RESOURCES  There is not enough pool memory to store the matching results.
264   @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.
265 
266 **/
267 EFI_STATUS
GetFvbCountAndBuffer(OUT UINTN * NumberHandles,OUT EFI_HANDLE ** Buffer)268 GetFvbCountAndBuffer (
269   OUT UINTN                               *NumberHandles,
270   OUT EFI_HANDLE                          **Buffer
271   )
272 {
273   EFI_STATUS                              Status;
274   UINTN                                   BufferSize;
275 
276   if ((NumberHandles == NULL) || (Buffer == NULL)) {
277     return EFI_INVALID_PARAMETER;
278   }
279 
280   BufferSize     = 0;
281   *NumberHandles = 0;
282   *Buffer        = NULL;
283   Status = gMmst->MmLocateHandle (
284                     ByProtocol,
285                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
286                     NULL,
287                     &BufferSize,
288                     *Buffer
289                     );
290   if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {
291     return EFI_NOT_FOUND;
292   }
293 
294   *Buffer = AllocatePool (BufferSize);
295   if (*Buffer == NULL) {
296     return EFI_OUT_OF_RESOURCES;
297   }
298 
299   Status = gMmst->MmLocateHandle (
300                     ByProtocol,
301                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,
302                     NULL,
303                     &BufferSize,
304                     *Buffer
305                     );
306 
307   *NumberHandles = BufferSize / sizeof(EFI_HANDLE);
308   if (EFI_ERROR(Status)) {
309     *NumberHandles = 0;
310     FreePool (*Buffer);
311     *Buffer = NULL;
312   }
313 
314   return Status;
315 }
316 
317 
318 /**
319   Get the variable statistics information from the information buffer pointed by gVariableInfo.
320 
321   Caution: This function may be invoked at SMM runtime.
322   InfoEntry and InfoSize are external input. Care must be taken to make sure not security issue at runtime.
323 
324   @param[in, out]  InfoEntry    A pointer to the buffer of variable information entry.
325                                 On input, point to the variable information returned last time. if
326                                 InfoEntry->VendorGuid is zero, return the first information.
327                                 On output, point to the next variable information.
328   @param[in, out]  InfoSize     On input, the size of the variable information buffer.
329                                 On output, the returned variable information size.
330 
331   @retval EFI_SUCCESS           The variable information is found and returned successfully.
332   @retval EFI_UNSUPPORTED       No variable inoformation exists in variable driver. The
333                                 PcdVariableCollectStatistics should be set TRUE to support it.
334   @retval EFI_BUFFER_TOO_SMALL  The buffer is too small to hold the next variable information.
335   @retval EFI_INVALID_PARAMETER Input parameter is invalid.
336 
337 **/
338 EFI_STATUS
SmmVariableGetStatistics(IN OUT VARIABLE_INFO_ENTRY * InfoEntry,IN OUT UINTN * InfoSize)339 SmmVariableGetStatistics (
340   IN OUT VARIABLE_INFO_ENTRY                           *InfoEntry,
341   IN OUT UINTN                                         *InfoSize
342   )
343 {
344   VARIABLE_INFO_ENTRY                                  *VariableInfo;
345   UINTN                                                NameSize;
346   UINTN                                                StatisticsInfoSize;
347   CHAR16                                               *InfoName;
348   UINTN                                                InfoNameMaxSize;
349   EFI_GUID                                             VendorGuid;
350 
351   if (InfoEntry == NULL) {
352     return EFI_INVALID_PARAMETER;
353   }
354 
355   VariableInfo = gVariableInfo;
356   if (VariableInfo == NULL) {
357     return EFI_UNSUPPORTED;
358   }
359 
360   StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY);
361   if (*InfoSize < StatisticsInfoSize) {
362     *InfoSize = StatisticsInfoSize;
363     return EFI_BUFFER_TOO_SMALL;
364   }
365   InfoName = (CHAR16 *)(InfoEntry + 1);
366   InfoNameMaxSize = (*InfoSize - sizeof (VARIABLE_INFO_ENTRY));
367 
368   CopyGuid (&VendorGuid, &InfoEntry->VendorGuid);
369 
370   if (IsZeroGuid (&VendorGuid)) {
371     //
372     // Return the first variable info
373     //
374     NameSize = StrSize (VariableInfo->Name);
375     StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + NameSize;
376     if (*InfoSize < StatisticsInfoSize) {
377       *InfoSize = StatisticsInfoSize;
378       return EFI_BUFFER_TOO_SMALL;
379     }
380     CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
381     CopyMem (InfoName, VariableInfo->Name, NameSize);
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       NameSize = StrSize (VariableInfo->Name);
392       if (NameSize <= InfoNameMaxSize) {
393         if (CompareMem (VariableInfo->Name, InfoName, NameSize) == 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   NameSize = StrSize (VariableInfo->Name);
414   StatisticsInfoSize = sizeof (VARIABLE_INFO_ENTRY) + NameSize;
415   if (*InfoSize < StatisticsInfoSize) {
416     *InfoSize = StatisticsInfoSize;
417     return EFI_BUFFER_TOO_SMALL;
418   }
419 
420   CopyMem (InfoEntry, VariableInfo, sizeof (VARIABLE_INFO_ENTRY));
421   CopyMem (InfoName, VariableInfo->Name, NameSize);
422   *InfoSize = StatisticsInfoSize;
423 
424   return EFI_SUCCESS;
425 }
426 
427 
428 /**
429   Communication service SMI Handler entry.
430 
431   This SMI handler provides services for the variable wrapper driver.
432 
433   Caution: This function may receive untrusted input.
434   This variable data and communicate buffer are external input, so this function will do basic validation.
435   Each sub function VariableServiceGetVariable(), VariableServiceGetNextVariableName(),
436   VariableServiceSetVariable(), VariableServiceQueryVariableInfo(), ReclaimForOS(),
437   SmmVariableGetStatistics() should also do validation based on its own knowledge.
438 
439   @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().
440   @param[in]     RegisterContext Points to an optional handler context which was specified when the
441                                  handler was registered.
442   @param[in, out] CommBuffer     A pointer to a collection of data in memory that will
443                                  be conveyed from a non-SMM environment into an SMM environment.
444   @param[in, out] CommBufferSize The size of the CommBuffer.
445 
446   @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers
447                                               should still be called.
448   @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should
449                                               still be called.
450   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still
451                                               be called.
452   @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.
453 **/
454 EFI_STATUS
455 EFIAPI
SmmVariableHandler(IN EFI_HANDLE DispatchHandle,IN CONST VOID * RegisterContext,IN OUT VOID * CommBuffer,IN OUT UINTN * CommBufferSize)456 SmmVariableHandler (
457   IN     EFI_HANDLE                                       DispatchHandle,
458   IN     CONST VOID                                       *RegisterContext,
459   IN OUT VOID                                             *CommBuffer,
460   IN OUT UINTN                                            *CommBufferSize
461   )
462 {
463   EFI_STATUS                                              Status;
464   SMM_VARIABLE_COMMUNICATE_HEADER                         *SmmVariableFunctionHeader;
465   SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE                *SmmVariableHeader;
466   SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME         *GetNextVariableName;
467   SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO            *QueryVariableInfo;
468   SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE               *GetPayloadSize;
469   SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *RuntimeVariableCacheContext;
470   SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO         *GetRuntimeCacheInfo;
471   SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE                  *VariableToLock;
472   SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY    *CommVariableProperty;
473   VARIABLE_INFO_ENTRY                                     *VariableInfo;
474   VARIABLE_RUNTIME_CACHE_CONTEXT                          *VariableCacheContext;
475   VARIABLE_STORE_HEADER                                   *VariableCache;
476   UINTN                                                   InfoSize;
477   UINTN                                                   NameBufferSize;
478   UINTN                                                   CommBufferPayloadSize;
479   UINTN                                                   TempCommBufferSize;
480 
481   //
482   // If input is invalid, stop processing this SMI
483   //
484   if (CommBuffer == NULL || CommBufferSize == NULL) {
485     return EFI_SUCCESS;
486   }
487 
488   TempCommBufferSize = *CommBufferSize;
489 
490   if (TempCommBufferSize < SMM_VARIABLE_COMMUNICATE_HEADER_SIZE) {
491     DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer size invalid!\n"));
492     return EFI_SUCCESS;
493   }
494   CommBufferPayloadSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
495   if (CommBufferPayloadSize > mVariableBufferPayloadSize) {
496     DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer payload size invalid!\n"));
497     return EFI_SUCCESS;
498   }
499 
500   if (!VariableSmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {
501     DEBUG ((EFI_D_ERROR, "SmmVariableHandler: SMM communication buffer in SMRAM or overflow!\n"));
502     return EFI_SUCCESS;
503   }
504 
505   SmmVariableFunctionHeader = (SMM_VARIABLE_COMMUNICATE_HEADER *)CommBuffer;
506   switch (SmmVariableFunctionHeader->Function) {
507     case SMM_VARIABLE_FUNCTION_GET_VARIABLE:
508       if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
509         DEBUG ((EFI_D_ERROR, "GetVariable: SMM communication buffer size invalid!\n"));
510         return EFI_SUCCESS;
511       }
512       //
513       // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
514       //
515       CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
516       SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
517       if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
518          ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
519         //
520         // Prevent InfoSize overflow happen
521         //
522         Status = EFI_ACCESS_DENIED;
523         goto EXIT;
524       }
525       InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
526                  + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
527 
528       //
529       // SMRAM range check already covered before
530       //
531       if (InfoSize > CommBufferPayloadSize) {
532         DEBUG ((EFI_D_ERROR, "GetVariable: Data size exceed communication buffer size limit!\n"));
533         Status = EFI_ACCESS_DENIED;
534         goto EXIT;
535       }
536 
537       //
538       // The VariableSpeculationBarrier() call here is to ensure the previous
539       // range/content checks for the CommBuffer have been completed before the
540       // subsequent consumption of the CommBuffer content.
541       //
542       VariableSpeculationBarrier ();
543       if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
544         //
545         // Make sure VariableName is A Null-terminated string.
546         //
547         Status = EFI_ACCESS_DENIED;
548         goto EXIT;
549       }
550 
551       Status = VariableServiceGetVariable (
552                  SmmVariableHeader->Name,
553                  &SmmVariableHeader->Guid,
554                  &SmmVariableHeader->Attributes,
555                  &SmmVariableHeader->DataSize,
556                  (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
557                  );
558       CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
559       break;
560 
561     case SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME:
562       if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
563         DEBUG ((EFI_D_ERROR, "GetNextVariableName: SMM communication buffer size invalid!\n"));
564         return EFI_SUCCESS;
565       }
566       //
567       // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
568       //
569       CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
570       GetNextVariableName = (SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME *) mVariableBufferPayload;
571       if ((UINTN)(~0) - GetNextVariableName->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name)) {
572         //
573         // Prevent InfoSize overflow happen
574         //
575         Status = EFI_ACCESS_DENIED;
576         goto EXIT;
577       }
578       InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name) + GetNextVariableName->NameSize;
579 
580       //
581       // SMRAM range check already covered before
582       //
583       if (InfoSize > CommBufferPayloadSize) {
584         DEBUG ((EFI_D_ERROR, "GetNextVariableName: Data size exceed communication buffer size limit!\n"));
585         Status = EFI_ACCESS_DENIED;
586         goto EXIT;
587       }
588 
589       NameBufferSize = CommBufferPayloadSize - OFFSET_OF(SMM_VARIABLE_COMMUNICATE_GET_NEXT_VARIABLE_NAME, Name);
590       if (NameBufferSize < sizeof (CHAR16) || GetNextVariableName->Name[NameBufferSize/sizeof (CHAR16) - 1] != L'\0') {
591         //
592         // Make sure input VariableName is A Null-terminated string.
593         //
594         Status = EFI_ACCESS_DENIED;
595         goto EXIT;
596       }
597 
598       Status = VariableServiceGetNextVariableName (
599                  &GetNextVariableName->NameSize,
600                  GetNextVariableName->Name,
601                  &GetNextVariableName->Guid
602                  );
603       CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
604       break;
605 
606     case SMM_VARIABLE_FUNCTION_SET_VARIABLE:
607       if (CommBufferPayloadSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) {
608         DEBUG ((EFI_D_ERROR, "SetVariable: SMM communication buffer size invalid!\n"));
609         return EFI_SUCCESS;
610       }
611       //
612       // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
613       //
614       CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
615       SmmVariableHeader = (SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE *) mVariableBufferPayload;
616       if (((UINTN)(~0) - SmmVariableHeader->DataSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)) ||
617          ((UINTN)(~0) - SmmVariableHeader->NameSize < OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name) + SmmVariableHeader->DataSize)) {
618         //
619         // Prevent InfoSize overflow happen
620         //
621         Status = EFI_ACCESS_DENIED;
622         goto EXIT;
623       }
624       InfoSize = OFFSET_OF(SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE, Name)
625                  + SmmVariableHeader->DataSize + SmmVariableHeader->NameSize;
626 
627       //
628       // SMRAM range check already covered before
629       // Data buffer should not contain SMM range
630       //
631       if (InfoSize > CommBufferPayloadSize) {
632         DEBUG ((EFI_D_ERROR, "SetVariable: Data size exceed communication buffer size limit!\n"));
633         Status = EFI_ACCESS_DENIED;
634         goto EXIT;
635       }
636 
637       //
638       // The VariableSpeculationBarrier() call here is to ensure the previous
639       // range/content checks for the CommBuffer have been completed before the
640       // subsequent consumption of the CommBuffer content.
641       //
642       VariableSpeculationBarrier ();
643       if (SmmVariableHeader->NameSize < sizeof (CHAR16) || SmmVariableHeader->Name[SmmVariableHeader->NameSize/sizeof (CHAR16) - 1] != L'\0') {
644         //
645         // Make sure VariableName is A Null-terminated string.
646         //
647         Status = EFI_ACCESS_DENIED;
648         goto EXIT;
649       }
650 
651       Status = VariableServiceSetVariable (
652                  SmmVariableHeader->Name,
653                  &SmmVariableHeader->Guid,
654                  SmmVariableHeader->Attributes,
655                  SmmVariableHeader->DataSize,
656                  (UINT8 *)SmmVariableHeader->Name + SmmVariableHeader->NameSize
657                  );
658       break;
659 
660     case SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO:
661       if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO)) {
662         DEBUG ((EFI_D_ERROR, "QueryVariableInfo: SMM communication buffer size invalid!\n"));
663         return EFI_SUCCESS;
664       }
665       QueryVariableInfo = (SMM_VARIABLE_COMMUNICATE_QUERY_VARIABLE_INFO *) SmmVariableFunctionHeader->Data;
666 
667       Status = VariableServiceQueryVariableInfo (
668                  QueryVariableInfo->Attributes,
669                  &QueryVariableInfo->MaximumVariableStorageSize,
670                  &QueryVariableInfo->RemainingVariableStorageSize,
671                  &QueryVariableInfo->MaximumVariableSize
672                  );
673       break;
674 
675     case SMM_VARIABLE_FUNCTION_GET_PAYLOAD_SIZE:
676       if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE)) {
677         DEBUG ((EFI_D_ERROR, "GetPayloadSize: SMM communication buffer size invalid!\n"));
678         return EFI_SUCCESS;
679       }
680       GetPayloadSize = (SMM_VARIABLE_COMMUNICATE_GET_PAYLOAD_SIZE *) SmmVariableFunctionHeader->Data;
681       GetPayloadSize->VariablePayloadSize = mVariableBufferPayloadSize;
682       Status = EFI_SUCCESS;
683       break;
684 
685     case SMM_VARIABLE_FUNCTION_READY_TO_BOOT:
686       if (AtRuntime()) {
687         Status = EFI_UNSUPPORTED;
688         break;
689       }
690       if (!mEndOfDxe) {
691         MorLockInitAtEndOfDxe ();
692         mEndOfDxe = TRUE;
693         VarCheckLibInitializeAtEndOfDxe (NULL);
694         //
695         // The initialization for variable quota.
696         //
697         InitializeVariableQuota ();
698       }
699       ReclaimForOS ();
700       Status = EFI_SUCCESS;
701       break;
702 
703     case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE:
704       mAtRuntime = TRUE;
705       Status = EFI_SUCCESS;
706       break;
707 
708     case SMM_VARIABLE_FUNCTION_GET_STATISTICS:
709       VariableInfo = (VARIABLE_INFO_ENTRY *) SmmVariableFunctionHeader->Data;
710       InfoSize = TempCommBufferSize - SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
711 
712       //
713       // Do not need to check SmmVariableFunctionHeader->Data in SMRAM here.
714       // It is covered by previous CommBuffer check
715       //
716 
717       //
718       // Do not need to check CommBufferSize buffer as it should point to SMRAM
719       // that was used by SMM core to cache CommSize from SmmCommunication protocol.
720       //
721 
722       Status = SmmVariableGetStatistics (VariableInfo, &InfoSize);
723       *CommBufferSize = InfoSize + SMM_VARIABLE_COMMUNICATE_HEADER_SIZE;
724       break;
725 
726     case SMM_VARIABLE_FUNCTION_LOCK_VARIABLE:
727       if (mEndOfDxe) {
728         Status = EFI_ACCESS_DENIED;
729       } else {
730         VariableToLock = (SMM_VARIABLE_COMMUNICATE_LOCK_VARIABLE *) SmmVariableFunctionHeader->Data;
731         Status = VariableLockRequestToLock (
732                    NULL,
733                    VariableToLock->Name,
734                    &VariableToLock->Guid
735                    );
736       }
737       break;
738     case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET:
739       if (mEndOfDxe) {
740         Status = EFI_ACCESS_DENIED;
741       } else {
742         CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) SmmVariableFunctionHeader->Data;
743         Status = VarCheckVariablePropertySet (
744                    CommVariableProperty->Name,
745                    &CommVariableProperty->Guid,
746                    &CommVariableProperty->VariableProperty
747                    );
748       }
749       break;
750     case SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET:
751       if (CommBufferPayloadSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
752         DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: SMM communication buffer size invalid!\n"));
753         return EFI_SUCCESS;
754       }
755       //
756       // Copy the input communicate buffer payload to pre-allocated SMM variable buffer payload.
757       //
758       CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
759       CommVariableProperty = (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY *) mVariableBufferPayload;
760       if ((UINTN) (~0) - CommVariableProperty->NameSize < OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name)) {
761         //
762         // Prevent InfoSize overflow happen
763         //
764         Status = EFI_ACCESS_DENIED;
765         goto EXIT;
766       }
767       InfoSize = OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) + CommVariableProperty->NameSize;
768 
769       //
770       // SMRAM range check already covered before
771       //
772       if (InfoSize > CommBufferPayloadSize) {
773         DEBUG ((EFI_D_ERROR, "VarCheckVariablePropertyGet: Data size exceed communication buffer size limit!\n"));
774         Status = EFI_ACCESS_DENIED;
775         goto EXIT;
776       }
777 
778       //
779       // The VariableSpeculationBarrier() call here is to ensure the previous
780       // range/content checks for the CommBuffer have been completed before the
781       // subsequent consumption of the CommBuffer content.
782       //
783       VariableSpeculationBarrier ();
784       if (CommVariableProperty->NameSize < sizeof (CHAR16) || CommVariableProperty->Name[CommVariableProperty->NameSize/sizeof (CHAR16) - 1] != L'\0') {
785         //
786         // Make sure VariableName is A Null-terminated string.
787         //
788         Status = EFI_ACCESS_DENIED;
789         goto EXIT;
790       }
791 
792       Status = VarCheckVariablePropertyGet (
793                  CommVariableProperty->Name,
794                  &CommVariableProperty->Guid,
795                  &CommVariableProperty->VariableProperty
796                  );
797       CopyMem (SmmVariableFunctionHeader->Data, mVariableBufferPayload, CommBufferPayloadSize);
798       break;
799     case SMM_VARIABLE_FUNCTION_INIT_RUNTIME_VARIABLE_CACHE_CONTEXT:
800       if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT)) {
801         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: SMM communication buffer size invalid!\n"));
802         Status = EFI_ACCESS_DENIED;
803         goto EXIT;
804       }
805       if (mEndOfDxe) {
806         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Cannot init context after end of DXE!\n"));
807         Status = EFI_ACCESS_DENIED;
808         goto EXIT;
809       }
810 
811       //
812       // Copy the input communicate buffer payload to the pre-allocated SMM variable payload buffer.
813       //
814       CopyMem (mVariableBufferPayload, SmmVariableFunctionHeader->Data, CommBufferPayloadSize);
815       RuntimeVariableCacheContext = (SMM_VARIABLE_COMMUNICATE_RUNTIME_VARIABLE_CACHE_CONTEXT *) mVariableBufferPayload;
816 
817       //
818       // Verify required runtime cache buffers are provided.
819       //
820       if (RuntimeVariableCacheContext->RuntimeVolatileCache == NULL ||
821           RuntimeVariableCacheContext->RuntimeNvCache == NULL ||
822           RuntimeVariableCacheContext->PendingUpdate == NULL ||
823           RuntimeVariableCacheContext->ReadLock == NULL ||
824           RuntimeVariableCacheContext->HobFlushComplete == NULL) {
825         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Required runtime cache buffer is NULL!\n"));
826         Status = EFI_ACCESS_DENIED;
827         goto EXIT;
828       }
829 
830       //
831       // Verify minimum size requirements for the runtime variable store buffers.
832       //
833       if ((RuntimeVariableCacheContext->RuntimeHobCache != NULL &&
834           RuntimeVariableCacheContext->RuntimeHobCache->Size < sizeof (VARIABLE_STORE_HEADER)) ||
835           RuntimeVariableCacheContext->RuntimeVolatileCache->Size < sizeof (VARIABLE_STORE_HEADER) ||
836           RuntimeVariableCacheContext->RuntimeNvCache->Size < sizeof (VARIABLE_STORE_HEADER)) {
837         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: A runtime cache buffer size is invalid!\n"));
838         Status = EFI_ACCESS_DENIED;
839         goto EXIT;
840       }
841 
842       //
843       // Verify runtime buffers do not overlap with SMRAM ranges.
844       //
845       if (RuntimeVariableCacheContext->RuntimeHobCache != NULL &&
846           !VariableSmmIsBufferOutsideSmmValid (
847             (UINTN) RuntimeVariableCacheContext->RuntimeHobCache,
848             (UINTN) RuntimeVariableCacheContext->RuntimeHobCache->Size)) {
849         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime HOB cache buffer in SMRAM or overflow!\n"));
850         Status = EFI_ACCESS_DENIED;
851         goto EXIT;
852       }
853       if (!VariableSmmIsBufferOutsideSmmValid (
854             (UINTN) RuntimeVariableCacheContext->RuntimeVolatileCache,
855             (UINTN) RuntimeVariableCacheContext->RuntimeVolatileCache->Size)) {
856         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime volatile cache buffer in SMRAM or overflow!\n"));
857         Status = EFI_ACCESS_DENIED;
858         goto EXIT;
859       }
860       if (!VariableSmmIsBufferOutsideSmmValid (
861             (UINTN) RuntimeVariableCacheContext->RuntimeNvCache,
862             (UINTN) RuntimeVariableCacheContext->RuntimeNvCache->Size)) {
863         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime non-volatile cache buffer in SMRAM or overflow!\n"));
864         Status = EFI_ACCESS_DENIED;
865         goto EXIT;
866       }
867       if (!VariableSmmIsBufferOutsideSmmValid (
868             (UINTN) RuntimeVariableCacheContext->PendingUpdate,
869             sizeof (*(RuntimeVariableCacheContext->PendingUpdate)))) {
870         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache pending update buffer in SMRAM or overflow!\n"));
871         Status = EFI_ACCESS_DENIED;
872         goto EXIT;
873       }
874       if (!VariableSmmIsBufferOutsideSmmValid (
875             (UINTN) RuntimeVariableCacheContext->ReadLock,
876             sizeof (*(RuntimeVariableCacheContext->ReadLock)))) {
877         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache read lock buffer in SMRAM or overflow!\n"));
878         Status = EFI_ACCESS_DENIED;
879         goto EXIT;
880       }
881       if (!VariableSmmIsBufferOutsideSmmValid (
882             (UINTN) RuntimeVariableCacheContext->HobFlushComplete,
883             sizeof (*(RuntimeVariableCacheContext->HobFlushComplete)))) {
884         DEBUG ((DEBUG_ERROR, "InitRuntimeVariableCacheContext: Runtime cache HOB flush complete buffer in SMRAM or overflow!\n"));
885         Status = EFI_ACCESS_DENIED;
886         goto EXIT;
887       }
888 
889       VariableCacheContext = &mVariableModuleGlobal->VariableGlobal.VariableRuntimeCacheContext;
890       VariableCacheContext->VariableRuntimeHobCache.Store      = RuntimeVariableCacheContext->RuntimeHobCache;
891       VariableCacheContext->VariableRuntimeVolatileCache.Store = RuntimeVariableCacheContext->RuntimeVolatileCache;
892       VariableCacheContext->VariableRuntimeNvCache.Store       = RuntimeVariableCacheContext->RuntimeNvCache;
893       VariableCacheContext->PendingUpdate                      = RuntimeVariableCacheContext->PendingUpdate;
894       VariableCacheContext->ReadLock                           = RuntimeVariableCacheContext->ReadLock;
895       VariableCacheContext->HobFlushComplete                   = RuntimeVariableCacheContext->HobFlushComplete;
896 
897       // Set up the intial pending request since the RT cache needs to be in sync with SMM cache
898       VariableCacheContext->VariableRuntimeHobCache.PendingUpdateOffset = 0;
899       VariableCacheContext->VariableRuntimeHobCache.PendingUpdateLength = 0;
900       if (mVariableModuleGlobal->VariableGlobal.HobVariableBase > 0 &&
901           VariableCacheContext->VariableRuntimeHobCache.Store != NULL) {
902         VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase;
903         VariableCacheContext->VariableRuntimeHobCache.PendingUpdateLength = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache);
904         CopyGuid (&(VariableCacheContext->VariableRuntimeHobCache.Store->Signature), &(VariableCache->Signature));
905       }
906       VariableCache = (VARIABLE_STORE_HEADER  *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
907       VariableCacheContext->VariableRuntimeVolatileCache.PendingUpdateOffset   = 0;
908       VariableCacheContext->VariableRuntimeVolatileCache.PendingUpdateLength   = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache);
909       CopyGuid (&(VariableCacheContext->VariableRuntimeVolatileCache.Store->Signature), &(VariableCache->Signature));
910 
911       VariableCache = (VARIABLE_STORE_HEADER  *) (UINTN) mNvVariableCache;
912       VariableCacheContext->VariableRuntimeNvCache.PendingUpdateOffset = 0;
913       VariableCacheContext->VariableRuntimeNvCache.PendingUpdateLength = (UINT32) ((UINTN) GetEndPointer (VariableCache) - (UINTN) VariableCache);
914       CopyGuid (&(VariableCacheContext->VariableRuntimeNvCache.Store->Signature), &(VariableCache->Signature));
915 
916       *(VariableCacheContext->PendingUpdate) = TRUE;
917       *(VariableCacheContext->ReadLock) = FALSE;
918       *(VariableCacheContext->HobFlushComplete) = FALSE;
919 
920       Status = EFI_SUCCESS;
921       break;
922     case SMM_VARIABLE_FUNCTION_SYNC_RUNTIME_CACHE:
923       Status = FlushPendingRuntimeVariableCacheUpdates ();
924       break;
925     case SMM_VARIABLE_FUNCTION_GET_RUNTIME_CACHE_INFO:
926       if (CommBufferPayloadSize < sizeof (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO)) {
927         DEBUG ((DEBUG_ERROR, "GetRuntimeCacheInfo: SMM communication buffer size invalid!\n"));
928         return EFI_SUCCESS;
929       }
930       GetRuntimeCacheInfo = (SMM_VARIABLE_COMMUNICATE_GET_RUNTIME_CACHE_INFO *) SmmVariableFunctionHeader->Data;
931 
932       if (mVariableModuleGlobal->VariableGlobal.HobVariableBase > 0) {
933         VariableCache = (VARIABLE_STORE_HEADER *) (UINTN) mVariableModuleGlobal->VariableGlobal.HobVariableBase;
934         GetRuntimeCacheInfo->TotalHobStorageSize = VariableCache->Size;
935       } else {
936         GetRuntimeCacheInfo->TotalHobStorageSize = 0;
937       }
938 
939       VariableCache = (VARIABLE_STORE_HEADER  *) (UINTN) mVariableModuleGlobal->VariableGlobal.VolatileVariableBase;
940       GetRuntimeCacheInfo->TotalVolatileStorageSize = VariableCache->Size;
941       VariableCache = (VARIABLE_STORE_HEADER  *) (UINTN) mNvVariableCache;
942       GetRuntimeCacheInfo->TotalNvStorageSize = (UINTN) VariableCache->Size;
943       GetRuntimeCacheInfo->AuthenticatedVariableUsage = mVariableModuleGlobal->VariableGlobal.AuthFormat;
944 
945       Status = EFI_SUCCESS;
946       break;
947 
948     default:
949       Status = EFI_UNSUPPORTED;
950   }
951 
952 EXIT:
953 
954   SmmVariableFunctionHeader->ReturnStatus = Status;
955 
956   return EFI_SUCCESS;
957 }
958 
959 /**
960   SMM END_OF_DXE protocol notification event handler.
961 
962   @param  Protocol   Points to the protocol's unique identifier
963   @param  Interface  Points to the interface instance
964   @param  Handle     The handle on which the interface was installed
965 
966   @retval EFI_SUCCESS   SmmEndOfDxeCallback runs successfully
967 
968 **/
969 EFI_STATUS
970 EFIAPI
SmmEndOfDxeCallback(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)971 SmmEndOfDxeCallback (
972   IN CONST EFI_GUID                       *Protocol,
973   IN VOID                                 *Interface,
974   IN EFI_HANDLE                           Handle
975   )
976 {
977   DEBUG ((EFI_D_INFO, "[Variable]SMM_END_OF_DXE is signaled\n"));
978   MorLockInitAtEndOfDxe ();
979   mEndOfDxe = TRUE;
980   VarCheckLibInitializeAtEndOfDxe (NULL);
981   //
982   // The initialization for variable quota.
983   //
984   InitializeVariableQuota ();
985   if (PcdGetBool (PcdReclaimVariableSpaceAtEndOfDxe)) {
986     ReclaimForOS ();
987   }
988 
989   return EFI_SUCCESS;
990 }
991 
992 /**
993   Initializes variable write service for SMM.
994 
995 **/
996 VOID
VariableWriteServiceInitializeSmm(VOID)997 VariableWriteServiceInitializeSmm (
998   VOID
999   )
1000 {
1001   EFI_STATUS    Status;
1002 
1003   Status = VariableWriteServiceInitialize ();
1004   if (EFI_ERROR (Status)) {
1005     DEBUG ((DEBUG_ERROR, "Variable write service initialization failed. Status = %r\n", Status));
1006   }
1007 
1008   //
1009   // Notify the variable wrapper driver the variable write service is ready
1010   //
1011   VariableNotifySmmWriteReady ();
1012 }
1013 
1014 /**
1015   SMM Fault Tolerant Write protocol notification event handler.
1016 
1017   Non-Volatile variable write may needs FTW protocol to reclaim when
1018   writting variable.
1019 
1020   @param  Protocol   Points to the protocol's unique identifier
1021   @param  Interface  Points to the interface instance
1022   @param  Handle     The handle on which the interface was installed
1023 
1024   @retval EFI_SUCCESS   SmmEventCallback runs successfully
1025   @retval EFI_NOT_FOUND The Fvb protocol for variable is not found.
1026 
1027  **/
1028 EFI_STATUS
1029 EFIAPI
SmmFtwNotificationEvent(IN CONST EFI_GUID * Protocol,IN VOID * Interface,IN EFI_HANDLE Handle)1030 SmmFtwNotificationEvent (
1031   IN CONST EFI_GUID                       *Protocol,
1032   IN VOID                                 *Interface,
1033   IN EFI_HANDLE                           Handle
1034   )
1035 {
1036   EFI_STATUS                              Status;
1037   EFI_PHYSICAL_ADDRESS                    VariableStoreBase;
1038   EFI_SMM_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *FvbProtocol;
1039   EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL   *FtwProtocol;
1040   EFI_PHYSICAL_ADDRESS                    NvStorageVariableBase;
1041   UINTN                                   FtwMaxBlockSize;
1042 
1043   if (mVariableModuleGlobal->FvbInstance != NULL) {
1044     return EFI_SUCCESS;
1045   }
1046 
1047   //
1048   // Ensure SMM FTW protocol is installed.
1049   //
1050   Status = GetFtwProtocol ((VOID **)&FtwProtocol);
1051   if (EFI_ERROR (Status)) {
1052     return Status;
1053   }
1054 
1055   Status = FtwProtocol->GetMaxBlockSize (FtwProtocol, &FtwMaxBlockSize);
1056   if (!EFI_ERROR (Status)) {
1057     ASSERT (PcdGet32 (PcdFlashNvStorageVariableSize) <= FtwMaxBlockSize);
1058   }
1059 
1060   NvStorageVariableBase = NV_STORAGE_VARIABLE_BASE;
1061   VariableStoreBase = NvStorageVariableBase + mNvFvHeaderCache->HeaderLength;
1062 
1063   //
1064   // Let NonVolatileVariableBase point to flash variable store base directly after FTW ready.
1065   //
1066   mVariableModuleGlobal->VariableGlobal.NonVolatileVariableBase = VariableStoreBase;
1067 
1068   //
1069   // Find the proper FVB protocol for variable.
1070   //
1071   Status = GetFvbInfoByAddress (NvStorageVariableBase, NULL, &FvbProtocol);
1072   if (EFI_ERROR (Status)) {
1073     return EFI_NOT_FOUND;
1074   }
1075 
1076   mVariableModuleGlobal->FvbInstance = FvbProtocol;
1077 
1078   //
1079   // Initializes variable write service after FTW was ready.
1080   //
1081   VariableWriteServiceInitializeSmm ();
1082 
1083   return EFI_SUCCESS;
1084 }
1085 
1086 
1087 /**
1088   Variable Driver main entry point. The Variable driver places the 4 EFI
1089   runtime services in the EFI System Table and installs arch protocols
1090   for variable read and write services being available. It also registers
1091   a notification function for an EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
1092 
1093   @retval EFI_SUCCESS       Variable service successfully initialized.
1094 
1095 **/
1096 EFI_STATUS
1097 EFIAPI
MmVariableServiceInitialize(VOID)1098 MmVariableServiceInitialize (
1099   VOID
1100   )
1101 {
1102   EFI_STATUS                              Status;
1103   EFI_HANDLE                              VariableHandle;
1104   VOID                                    *SmmFtwRegistration;
1105   VOID                                    *SmmEndOfDxeRegistration;
1106 
1107   //
1108   // Variable initialize.
1109   //
1110   Status = VariableCommonInitialize ();
1111   ASSERT_EFI_ERROR (Status);
1112 
1113   //
1114   // Install the Smm Variable Protocol on a new handle.
1115   //
1116   VariableHandle = NULL;
1117   Status = gMmst->MmInstallProtocolInterface (
1118                     &VariableHandle,
1119                     &gEfiSmmVariableProtocolGuid,
1120                     EFI_NATIVE_INTERFACE,
1121                     &gSmmVariable
1122                     );
1123   ASSERT_EFI_ERROR (Status);
1124 
1125   Status = gMmst->MmInstallProtocolInterface (
1126                     &VariableHandle,
1127                     &gEdkiiSmmVarCheckProtocolGuid,
1128                     EFI_NATIVE_INTERFACE,
1129                     &mSmmVarCheck
1130                     );
1131   ASSERT_EFI_ERROR (Status);
1132 
1133   mVariableBufferPayloadSize =  GetMaxVariableSize () +
1134                                   OFFSET_OF (SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY, Name) -
1135                                   GetVariableHeaderSize (mVariableModuleGlobal->VariableGlobal.AuthFormat);
1136 
1137   Status = gMmst->MmAllocatePool (
1138                     EfiRuntimeServicesData,
1139                     mVariableBufferPayloadSize,
1140                     (VOID **)&mVariableBufferPayload
1141                     );
1142   ASSERT_EFI_ERROR (Status);
1143 
1144   ///
1145   /// Register SMM variable SMI handler
1146   ///
1147   VariableHandle = NULL;
1148   Status = gMmst->MmiHandlerRegister (SmmVariableHandler, &gEfiSmmVariableProtocolGuid, &VariableHandle);
1149   ASSERT_EFI_ERROR (Status);
1150 
1151   //
1152   // Notify the variable wrapper driver the variable service is ready
1153   //
1154   VariableNotifySmmReady ();
1155 
1156   //
1157   // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
1158   //
1159   Status = gMmst->MmRegisterProtocolNotify (
1160                     &gEfiMmEndOfDxeProtocolGuid,
1161                     SmmEndOfDxeCallback,
1162                     &SmmEndOfDxeRegistration
1163                     );
1164   ASSERT_EFI_ERROR (Status);
1165 
1166   if (!PcdGetBool (PcdEmuVariableNvModeEnable)) {
1167     //
1168     // Register FtwNotificationEvent () notify function.
1169     //
1170     Status = gMmst->MmRegisterProtocolNotify (
1171                       &gEfiSmmFaultTolerantWriteProtocolGuid,
1172                       SmmFtwNotificationEvent,
1173                       &SmmFtwRegistration
1174                       );
1175     ASSERT_EFI_ERROR (Status);
1176 
1177     SmmFtwNotificationEvent (NULL, NULL, NULL);
1178   } else {
1179     //
1180     // Emulated non-volatile variable mode does not depend on FVB and FTW.
1181     //
1182     VariableWriteServiceInitializeSmm ();
1183   }
1184 
1185   return EFI_SUCCESS;
1186 }
1187 
1188 
1189