MdeModulePkg/FaultTolerantWriteDxe: factor out boot service accesses
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWriteDxe / FaultTolerantWriteSmm.c
1 /** @file\r
2 \r
3   This is a simple fault tolerant write driver that is intended to use in the SMM environment.\r
4 \r
5   This boot service protocol only provides fault tolerant write capability for\r
6   block devices.  The protocol has internal non-volatile intermediate storage\r
7   of the data and private information. It should be able to recover\r
8   automatically from a critical fault, such as power failure.\r
9 \r
10   The implementation uses an FTW (Fault Tolerant Write) Work Space.\r
11   This work space is a memory copy of the work space on the Working Block,\r
12   the size of the work space is the FTW_WORK_SPACE_SIZE bytes.\r
13 \r
14   The work space stores each write record as EFI_FTW_RECORD structure.\r
15   The spare block stores the write buffer before write to the target block.\r
16 \r
17   The write record has three states to specify the different phase of write operation.\r
18   1) WRITE_ALLOCATED is that the record is allocated in write space.\r
19      The information of write operation is stored in write record structure.\r
20   2) SPARE_COMPLETED is that the data from write buffer is writed into the spare block as the backup.\r
21   3) WRITE_COMPLETED is that the data is copied from the spare block to the target block.\r
22 \r
23   This driver operates the data as the whole size of spare block.\r
24   It first read the SpareAreaLength data from the target block into the spare memory buffer.\r
25   Then copy the write buffer data into the spare memory buffer.\r
26   Then write the spare memory buffer into the spare block.\r
27   Final copy the data from the spare block to the target block.\r
28 \r
29   To make this drive work well, the following conditions must be satisfied:\r
30   1. The write NumBytes data must be fit within Spare area.\r
31      Offset + NumBytes <= SpareAreaLength\r
32   2. The whole flash range has the same block size.\r
33   3. Working block is an area which contains working space in its last block and has the same size as spare block.\r
34   4. Working Block area must be in the single one Firmware Volume Block range which FVB protocol is produced on.\r
35   5. Spare area must be in the single one Firmware Volume Block range which FVB protocol is produced on.\r
36   6. Any write data area (SpareAreaLength Area) which the data will be written into must be\r
37      in the single one Firmware Volume Block range which FVB protocol is produced on.\r
38   7. If write data area (such as Variable range) is enlarged, the spare area range must be enlarged.\r
39      The spare area must be enough large to store the write data before write them into the target range.\r
40   If one of them is not satisfied, FtwWrite may fail.\r
41   Usually, Spare area only takes one block. That's SpareAreaLength = BlockSize, NumberOfSpareBlock = 1.\r
42 \r
43   Caution: This module requires additional review when modified.\r
44   This driver need to make sure the CommBuffer is not in the SMRAM range.\r
45 \r
46 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
47 This program and the accompanying materials\r
48 are licensed and made available under the terms and conditions of the BSD License\r
49 which accompanies this distribution.  The full text of the license may be found at\r
50 http://opensource.org/licenses/bsd-license.php\r
51 \r
52 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
53 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
54 \r
55 **/\r
56 \r
57 #include <PiMm.h>\r
58 #include <Library/MmServicesTableLib.h>\r
59 #include <Library/BaseLib.h>\r
60 #include <Protocol/SmmSwapAddressRange.h>\r
61 #include "FaultTolerantWrite.h"\r
62 #include "FaultTolerantWriteSmmCommon.h"\r
63 #include <Protocol/MmEndOfDxe.h>\r
64 \r
65 EFI_EVENT                                 mFvbRegistration = NULL;\r
66 EFI_FTW_DEVICE                            *mFtwDevice      = NULL;\r
67 \r
68 ///\r
69 /// The flag to indicate whether the platform has left the DXE phase of execution.\r
70 ///\r
71 BOOLEAN                                   mEndOfDxe = FALSE;\r
72 \r
73 /**\r
74   Retrieve the SMM FVB protocol interface by HANDLE.\r
75 \r
76   @param[in]  FvBlockHandle     The handle of SMM FVB protocol that provides services for\r
77                                 reading, writing, and erasing the target block.\r
78   @param[out] FvBlock           The interface of SMM FVB protocol\r
79 \r
80   @retval EFI_SUCCESS           The interface information for the specified protocol was returned.\r
81   @retval EFI_UNSUPPORTED       The device does not support the SMM FVB protocol.\r
82   @retval EFI_INVALID_PARAMETER FvBlockHandle is not a valid EFI_HANDLE or FvBlock is NULL.\r
83 \r
84 **/\r
85 EFI_STATUS\r
86 FtwGetFvbByHandle (\r
87   IN  EFI_HANDLE                          FvBlockHandle,\r
88   OUT EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  **FvBlock\r
89   )\r
90 {\r
91   //\r
92   // To get the SMM FVB protocol interface on the handle\r
93   //\r
94   return gMmst->MmHandleProtocol (\r
95                   FvBlockHandle,\r
96                   &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
97                   (VOID **) FvBlock\r
98                   );\r
99 }\r
100 \r
101 /**\r
102   Retrieve the SMM Swap Address Range protocol interface.\r
103 \r
104   @param[out] SarProtocol       The interface of SMM SAR protocol\r
105 \r
106   @retval EFI_SUCCESS           The SMM SAR protocol instance was found and returned in SarProtocol.\r
107   @retval EFI_NOT_FOUND         The SMM SAR protocol instance was not found.\r
108   @retval EFI_INVALID_PARAMETER SarProtocol is NULL.\r
109 \r
110 **/\r
111 EFI_STATUS\r
112 FtwGetSarProtocol (\r
113   OUT VOID                                **SarProtocol\r
114   )\r
115 {\r
116   EFI_STATUS                              Status;\r
117 \r
118   //\r
119   // Locate Smm Swap Address Range protocol\r
120   //\r
121   Status = gMmst->MmLocateProtocol (\r
122                     &gEfiSmmSwapAddressRangeProtocolGuid,\r
123                     NULL,\r
124                     SarProtocol\r
125                     );\r
126   return Status;\r
127 }\r
128 \r
129 /**\r
130   Function returns an array of handles that support the SMM FVB protocol\r
131   in a buffer allocated from pool.\r
132 \r
133   @param[out]  NumberHandles    The number of handles returned in Buffer.\r
134   @param[out]  Buffer           A pointer to the buffer to return the requested\r
135                                 array of  handles that support SMM FVB protocol.\r
136 \r
137   @retval EFI_SUCCESS           The array of handles was returned in Buffer, and the number of\r
138                                 handles in Buffer was returned in NumberHandles.\r
139   @retval EFI_NOT_FOUND         No SMM FVB handle was found.\r
140   @retval EFI_OUT_OF_RESOURCES  There is not enough pool memory to store the matching results.\r
141   @retval EFI_INVALID_PARAMETER NumberHandles is NULL or Buffer is NULL.\r
142 \r
143 **/\r
144 EFI_STATUS\r
145 GetFvbCountAndBuffer (\r
146   OUT UINTN                               *NumberHandles,\r
147   OUT EFI_HANDLE                          **Buffer\r
148   )\r
149 {\r
150   EFI_STATUS                              Status;\r
151   UINTN                                   BufferSize;\r
152 \r
153   if ((NumberHandles == NULL) || (Buffer == NULL)) {\r
154     return EFI_INVALID_PARAMETER;\r
155   }\r
156 \r
157   BufferSize     = 0;\r
158   *NumberHandles = 0;\r
159   *Buffer        = NULL;\r
160   Status = gMmst->MmLocateHandle (\r
161                     ByProtocol,\r
162                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
163                     NULL,\r
164                     &BufferSize,\r
165                     *Buffer\r
166                     );\r
167   if (EFI_ERROR(Status) && Status != EFI_BUFFER_TOO_SMALL) {\r
168     return EFI_NOT_FOUND;\r
169   }\r
170 \r
171   *Buffer = AllocatePool (BufferSize);\r
172   if (*Buffer == NULL) {\r
173     return EFI_OUT_OF_RESOURCES;\r
174   }\r
175 \r
176   Status = gMmst->MmLocateHandle (\r
177                     ByProtocol,\r
178                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
179                     NULL,\r
180                     &BufferSize,\r
181                     *Buffer\r
182                     );\r
183 \r
184   *NumberHandles = BufferSize / sizeof(EFI_HANDLE);\r
185   if (EFI_ERROR(Status)) {\r
186     *NumberHandles = 0;\r
187     FreePool (*Buffer);\r
188     *Buffer = NULL;\r
189   }\r
190 \r
191   return Status;\r
192 }\r
193 \r
194 \r
195 /**\r
196   Get the handle of the SMM FVB protocol by the FVB base address and attributes.\r
197 \r
198   @param[in]  Address       The base address of SMM FVB protocol.\r
199   @param[in]  Attributes    The attributes of the SMM FVB protocol.\r
200   @param[out] SmmFvbHandle  The handle of the SMM FVB protocol.\r
201 \r
202   @retval  EFI_SUCCESS    The FVB handle is found.\r
203   @retval  EFI_ABORTED    The FVB protocol is not found.\r
204 \r
205 **/\r
206 EFI_STATUS\r
207 GetFvbByAddressAndAttribute (\r
208   IN  EFI_PHYSICAL_ADDRESS            Address,\r
209   IN  EFI_FVB_ATTRIBUTES_2            Attributes,\r
210   OUT EFI_HANDLE                      *SmmFvbHandle\r
211   )\r
212 {\r
213   EFI_STATUS                          Status;\r
214   EFI_HANDLE                          *HandleBuffer;\r
215   UINTN                               HandleCount;\r
216   UINTN                               Index;\r
217   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;\r
218   EFI_FVB_ATTRIBUTES_2                FvbAttributes;\r
219   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;\r
220 \r
221   HandleBuffer = NULL;\r
222 \r
223   //\r
224   // Locate all handles of SMM Fvb protocol.\r
225   //\r
226   Status = GetFvbCountAndBuffer (&HandleCount, &HandleBuffer);\r
227   if (EFI_ERROR (Status)) {\r
228     return EFI_ABORTED;\r
229   }\r
230 \r
231   //\r
232   // Find the proper SMM Fvb handle by the address and attributes.\r
233   //\r
234   for (Index = 0; Index < HandleCount; Index++) {\r
235     Status = FtwGetFvbByHandle (HandleBuffer[Index], &Fvb);\r
236     if (EFI_ERROR (Status)) {\r
237       break;\r
238     }\r
239     //\r
240     // Compare the address.\r
241     //\r
242     Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);\r
243     if (EFI_ERROR (Status)) {\r
244       continue;\r
245     }\r
246     if (Address != FvbBaseAddress) {\r
247      continue;\r
248     }\r
249 \r
250     //\r
251     // Compare the attribute.\r
252     //\r
253     Status = Fvb->GetAttributes (Fvb, &FvbAttributes);\r
254     if (EFI_ERROR (Status)) {\r
255       continue;\r
256     }\r
257     if (Attributes != FvbAttributes) {\r
258      continue;\r
259     }\r
260 \r
261     //\r
262     // Found the proper FVB handle.\r
263     //\r
264     *SmmFvbHandle = HandleBuffer[Index];\r
265     FreePool (HandleBuffer);\r
266     return EFI_SUCCESS;\r
267   }\r
268 \r
269   FreePool (HandleBuffer);\r
270   return EFI_ABORTED;\r
271 }\r
272 \r
273 /**\r
274   Communication service SMI Handler entry.\r
275 \r
276   This SMI handler provides services for the fault tolerant write wrapper driver.\r
277 \r
278   Caution: This function requires additional review when modified.\r
279   This driver need to make sure the CommBuffer is not in the SMRAM range.\r
280   Also in FTW_FUNCTION_GET_LAST_WRITE case, check SmmFtwGetLastWriteHeader->Data +\r
281   SmmFtwGetLastWriteHeader->PrivateDataSize within communication buffer.\r
282 \r
283   @param[in]     DispatchHandle  The unique handle assigned to this handler by SmiHandlerRegister().\r
284   @param[in]     RegisterContext Points to an optional handler context which was specified when the\r
285                                  handler was registered.\r
286   @param[in, out] CommBuffer     A pointer to a collection of data in memory that will be conveyed\r
287                                  from a non-SMM environment into an SMM environment.\r
288   @param[in, out] CommBufferSize The size of the CommBuffer.\r
289 \r
290   @retval EFI_SUCCESS                         The interrupt was handled and quiesced. No other handlers\r
291                                               should still be called.\r
292   @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED  The interrupt has been quiesced but other handlers should\r
293                                               still be called.\r
294   @retval EFI_WARN_INTERRUPT_SOURCE_PENDING   The interrupt is still pending and other handlers should still\r
295                                               be called.\r
296   @retval EFI_INTERRUPT_PENDING               The interrupt could not be quiesced.\r
297 \r
298 **/\r
299 EFI_STATUS\r
300 EFIAPI\r
301 SmmFaultTolerantWriteHandler (\r
302   IN     EFI_HANDLE                                DispatchHandle,\r
303   IN     CONST VOID                                *RegisterContext,\r
304   IN OUT VOID                                      *CommBuffer,\r
305   IN OUT UINTN                                     *CommBufferSize\r
306   )\r
307 {\r
308   EFI_STATUS                                       Status;\r
309   SMM_FTW_COMMUNICATE_FUNCTION_HEADER              *SmmFtwFunctionHeader;\r
310   SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER                *SmmGetMaxBlockSizeHeader;\r
311   SMM_FTW_ALLOCATE_HEADER                          *SmmFtwAllocateHeader;\r
312   SMM_FTW_WRITE_HEADER                             *SmmFtwWriteHeader;\r
313   SMM_FTW_RESTART_HEADER                           *SmmFtwRestartHeader;\r
314   SMM_FTW_GET_LAST_WRITE_HEADER                    *SmmFtwGetLastWriteHeader;\r
315   VOID                                             *PrivateData;\r
316   EFI_HANDLE                                       SmmFvbHandle;\r
317   UINTN                                            InfoSize;\r
318   UINTN                                            CommBufferPayloadSize;\r
319   UINTN                                            PrivateDataSize;\r
320   UINTN                                            Length;\r
321   UINTN                                            TempCommBufferSize;\r
322 \r
323   //\r
324   // If input is invalid, stop processing this SMI\r
325   //\r
326   if (CommBuffer == NULL || CommBufferSize == NULL) {\r
327     return EFI_SUCCESS;\r
328   }\r
329 \r
330   TempCommBufferSize = *CommBufferSize;\r
331 \r
332   if (TempCommBufferSize < SMM_FTW_COMMUNICATE_HEADER_SIZE) {\r
333     DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer size invalid!\n"));\r
334     return EFI_SUCCESS;\r
335   }\r
336   CommBufferPayloadSize = TempCommBufferSize - SMM_FTW_COMMUNICATE_HEADER_SIZE;\r
337 \r
338   if (!FtwSmmIsBufferOutsideSmmValid ((UINTN)CommBuffer, TempCommBufferSize)) {\r
339     DEBUG ((EFI_D_ERROR, "SmmFtwHandler: SMM communication buffer in SMRAM or overflow!\n"));\r
340     return EFI_SUCCESS;\r
341   }\r
342 \r
343   SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *)CommBuffer;\r
344 \r
345   if (mEndOfDxe) {\r
346     //\r
347     // It will be not safe to expose the operations after End Of Dxe.\r
348     //\r
349     DEBUG ((EFI_D_ERROR, "SmmFtwHandler: Not safe to do the operation: %x after End Of Dxe, so access denied!\n", SmmFtwFunctionHeader->Function));\r
350     SmmFtwFunctionHeader->ReturnStatus = EFI_ACCESS_DENIED;\r
351     return EFI_SUCCESS;\r
352   }\r
353 \r
354   switch (SmmFtwFunctionHeader->Function) {\r
355     case FTW_FUNCTION_GET_MAX_BLOCK_SIZE:\r
356       if (CommBufferPayloadSize < sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER)) {\r
357         DEBUG ((EFI_D_ERROR, "GetMaxBlockSize: SMM communication buffer size invalid!\n"));\r
358         return EFI_SUCCESS;\r
359       }\r
360       SmmGetMaxBlockSizeHeader = (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER *) SmmFtwFunctionHeader->Data;\r
361 \r
362       Status = FtwGetMaxBlockSize (\r
363                  &mFtwDevice->FtwInstance,\r
364                  &SmmGetMaxBlockSizeHeader->BlockSize\r
365                  );\r
366       break;\r
367 \r
368     case FTW_FUNCTION_ALLOCATE:\r
369       if (CommBufferPayloadSize < sizeof (SMM_FTW_ALLOCATE_HEADER)) {\r
370         DEBUG ((EFI_D_ERROR, "Allocate: SMM communication buffer size invalid!\n"));\r
371         return EFI_SUCCESS;\r
372       }\r
373       SmmFtwAllocateHeader = (SMM_FTW_ALLOCATE_HEADER *) SmmFtwFunctionHeader->Data;\r
374       Status = FtwAllocate (\r
375                  &mFtwDevice->FtwInstance,\r
376                  &SmmFtwAllocateHeader->CallerId,\r
377                  SmmFtwAllocateHeader->PrivateDataSize,\r
378                  SmmFtwAllocateHeader->NumberOfWrites\r
379                  );\r
380       break;\r
381 \r
382     case FTW_FUNCTION_WRITE:\r
383       if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) {\r
384         DEBUG ((EFI_D_ERROR, "Write: SMM communication buffer size invalid!\n"));\r
385         return EFI_SUCCESS;\r
386       }\r
387       SmmFtwWriteHeader = (SMM_FTW_WRITE_HEADER *) SmmFtwFunctionHeader->Data;\r
388       Length = SmmFtwWriteHeader->Length;\r
389       PrivateDataSize = SmmFtwWriteHeader->PrivateDataSize;\r
390       if (((UINTN)(~0) - Length < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data)) ||\r
391         ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length)) {\r
392         //\r
393         // Prevent InfoSize overflow\r
394         //\r
395         Status = EFI_ACCESS_DENIED;\r
396         break;\r
397       }\r
398       InfoSize = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length + PrivateDataSize;\r
399 \r
400       //\r
401       // SMRAM range check already covered before\r
402       //\r
403       if (InfoSize > CommBufferPayloadSize) {\r
404         DEBUG ((EFI_D_ERROR, "Write: Data size exceed communication buffer size limit!\n"));\r
405         Status = EFI_ACCESS_DENIED;\r
406         break;\r
407       }\r
408 \r
409       if (PrivateDataSize == 0) {\r
410         PrivateData = NULL;\r
411       } else {\r
412         PrivateData = (VOID *)&SmmFtwWriteHeader->Data[Length];\r
413       }\r
414       Status = GetFvbByAddressAndAttribute (\r
415                  SmmFtwWriteHeader->FvbBaseAddress,\r
416                  SmmFtwWriteHeader->FvbAttributes,\r
417                  &SmmFvbHandle\r
418                  );\r
419       if (!EFI_ERROR (Status)) {\r
420         //\r
421         // The SpeculationBarrier() call here is to ensure the previous\r
422         // range/content checks for the CommBuffer have been completed before\r
423         // calling into FtwWrite().\r
424         //\r
425         SpeculationBarrier ();\r
426         Status = FtwWrite(\r
427                    &mFtwDevice->FtwInstance,\r
428                    SmmFtwWriteHeader->Lba,\r
429                    SmmFtwWriteHeader->Offset,\r
430                    Length,\r
431                    PrivateData,\r
432                    SmmFvbHandle,\r
433                    SmmFtwWriteHeader->Data\r
434                    );\r
435       }\r
436       break;\r
437 \r
438     case FTW_FUNCTION_RESTART:\r
439       if (CommBufferPayloadSize < sizeof (SMM_FTW_RESTART_HEADER)) {\r
440         DEBUG ((EFI_D_ERROR, "Restart: SMM communication buffer size invalid!\n"));\r
441         return EFI_SUCCESS;\r
442       }\r
443       SmmFtwRestartHeader = (SMM_FTW_RESTART_HEADER *) SmmFtwFunctionHeader->Data;\r
444       Status = GetFvbByAddressAndAttribute (\r
445                  SmmFtwRestartHeader->FvbBaseAddress,\r
446                  SmmFtwRestartHeader->FvbAttributes,\r
447                  &SmmFvbHandle\r
448                  );\r
449       if (!EFI_ERROR (Status)) {\r
450         Status = FtwRestart (&mFtwDevice->FtwInstance, SmmFvbHandle);\r
451       }\r
452       break;\r
453 \r
454     case FTW_FUNCTION_ABORT:\r
455       Status = FtwAbort (&mFtwDevice->FtwInstance);\r
456       break;\r
457 \r
458     case FTW_FUNCTION_GET_LAST_WRITE:\r
459       if (CommBufferPayloadSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)) {\r
460         DEBUG ((EFI_D_ERROR, "GetLastWrite: SMM communication buffer size invalid!\n"));\r
461         return EFI_SUCCESS;\r
462       }\r
463       SmmFtwGetLastWriteHeader = (SMM_FTW_GET_LAST_WRITE_HEADER *) SmmFtwFunctionHeader->Data;\r
464       PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;\r
465       if ((UINTN)(~0) - PrivateDataSize < OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data)){\r
466         //\r
467         // Prevent InfoSize overflow\r
468         //\r
469         Status = EFI_ACCESS_DENIED;\r
470         break;\r
471       }\r
472       InfoSize = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + PrivateDataSize;\r
473 \r
474       //\r
475       // SMRAM range check already covered before\r
476       //\r
477       if (InfoSize > CommBufferPayloadSize) {\r
478         DEBUG ((EFI_D_ERROR, "Data size exceed communication buffer size limit!\n"));\r
479         Status = EFI_ACCESS_DENIED;\r
480         break;\r
481       }\r
482 \r
483       Status = FtwGetLastWrite (\r
484                  &mFtwDevice->FtwInstance,\r
485                  &SmmFtwGetLastWriteHeader->CallerId,\r
486                  &SmmFtwGetLastWriteHeader->Lba,\r
487                  &SmmFtwGetLastWriteHeader->Offset,\r
488                  &SmmFtwGetLastWriteHeader->Length,\r
489                  &PrivateDataSize,\r
490                  (VOID *)SmmFtwGetLastWriteHeader->Data,\r
491                  &SmmFtwGetLastWriteHeader->Complete\r
492                  );\r
493       SmmFtwGetLastWriteHeader->PrivateDataSize = PrivateDataSize;\r
494       break;\r
495 \r
496     default:\r
497       Status = EFI_UNSUPPORTED;\r
498   }\r
499 \r
500   SmmFtwFunctionHeader->ReturnStatus = Status;\r
501 \r
502   return EFI_SUCCESS;\r
503 }\r
504 \r
505 \r
506 /**\r
507   SMM Firmware Volume Block Protocol notification event handler.\r
508 \r
509   @param[in]  Protocol      Points to the protocol's unique identifier\r
510   @param[in]  Interface     Points to the interface instance\r
511   @param[in]  Handle        The handle on which the interface was installed\r
512 \r
513   @retval EFI_SUCCESS       SmmEventCallback runs successfully\r
514 \r
515  **/\r
516 EFI_STATUS\r
517 EFIAPI\r
518 FvbNotificationEvent (\r
519   IN CONST EFI_GUID                       *Protocol,\r
520   IN VOID                                 *Interface,\r
521   IN EFI_HANDLE                           Handle\r
522   )\r
523 {\r
524   EFI_STATUS                              Status;\r
525   EFI_SMM_FAULT_TOLERANT_WRITE_PROTOCOL   *FtwProtocol;\r
526   EFI_HANDLE                              SmmFtwHandle;\r
527 \r
528   //\r
529   // Just return to avoid install SMM FaultTolerantWriteProtocol again\r
530   // if SMM Fault Tolerant Write protocol had been installed.\r
531   //\r
532   Status = gMmst->MmLocateProtocol (\r
533                     &gEfiSmmFaultTolerantWriteProtocolGuid,\r
534                     NULL,\r
535                     (VOID **) &FtwProtocol\r
536                     );\r
537   if (!EFI_ERROR (Status)) {\r
538     return EFI_SUCCESS;\r
539   }\r
540 \r
541   //\r
542   // Found proper FVB protocol and initialize FtwDevice for protocol installation\r
543   //\r
544   Status = InitFtwProtocol (mFtwDevice);\r
545   if (EFI_ERROR(Status)) {\r
546     return Status;\r
547   }\r
548 \r
549   //\r
550   // Install protocol interface\r
551   //\r
552   Status = gMmst->MmInstallProtocolInterface (\r
553                     &mFtwDevice->Handle,\r
554                     &gEfiSmmFaultTolerantWriteProtocolGuid,\r
555                     EFI_NATIVE_INTERFACE,\r
556                     &mFtwDevice->FtwInstance\r
557                     );\r
558   ASSERT_EFI_ERROR (Status);\r
559 \r
560   ///\r
561   /// Register SMM FTW SMI handler\r
562   ///\r
563   Status = gMmst->MmiHandlerRegister (SmmFaultTolerantWriteHandler, &gEfiSmmFaultTolerantWriteProtocolGuid, &SmmFtwHandle);\r
564   ASSERT_EFI_ERROR (Status);\r
565 \r
566   //\r
567   // Notify the Ftw wrapper driver SMM Ftw is ready\r
568   //\r
569   FtwNotifySmmReady ();\r
570 \r
571   return EFI_SUCCESS;\r
572 }\r
573 \r
574 /**\r
575   SMM END_OF_DXE protocol notification event handler.\r
576 \r
577   @param  Protocol   Points to the protocol's unique identifier\r
578   @param  Interface  Points to the interface instance\r
579   @param  Handle     The handle on which the interface was installed\r
580 \r
581   @retval EFI_SUCCESS   SmmEndOfDxeCallback runs successfully\r
582 \r
583 **/\r
584 EFI_STATUS\r
585 EFIAPI\r
586 MmEndOfDxeCallback (\r
587   IN CONST EFI_GUID                       *Protocol,\r
588   IN VOID                                 *Interface,\r
589   IN EFI_HANDLE                           Handle\r
590   )\r
591 {\r
592   mEndOfDxe = TRUE;\r
593   return EFI_SUCCESS;\r
594 }\r
595 \r
596 /**\r
597   Shared entry point of the module\r
598 \r
599   @retval EFI_SUCCESS           The initialization finished successfully.\r
600   @retval EFI_OUT_OF_RESOURCES  Allocate memory error\r
601   @retval EFI_INVALID_PARAMETER Workspace or Spare block does not exist\r
602 **/\r
603 EFI_STATUS\r
604 MmFaultTolerantWriteInitialize (\r
605   VOID\r
606   )\r
607 {\r
608   EFI_STATUS                              Status;\r
609   VOID                                    *MmEndOfDxeRegistration;\r
610 \r
611   //\r
612   // Allocate private data structure for SMM FTW protocol and do some initialization\r
613   //\r
614   Status = InitFtwDevice (&mFtwDevice);\r
615   if (EFI_ERROR(Status)) {\r
616     return Status;\r
617   }\r
618 \r
619   //\r
620   // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.\r
621   //\r
622   Status = gMmst->MmRegisterProtocolNotify (\r
623                     &gEfiMmEndOfDxeProtocolGuid,\r
624                     MmEndOfDxeCallback,\r
625                     &MmEndOfDxeRegistration\r
626                     );\r
627   ASSERT_EFI_ERROR (Status);\r
628 \r
629   //\r
630   // Register FvbNotificationEvent () notify function.\r
631   //\r
632   Status = gMmst->MmRegisterProtocolNotify (\r
633                     &gEfiSmmFirmwareVolumeBlockProtocolGuid,\r
634                     FvbNotificationEvent,\r
635                     &mFvbRegistration\r
636                     );\r
637   ASSERT_EFI_ERROR (Status);\r
638 \r
639   FvbNotificationEvent (NULL, NULL, NULL);\r
640 \r
641   return EFI_SUCCESS;\r
642 }\r