1 /** @file
2 
3   Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW
4   module.
5 
6 Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved. <BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution.  The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "FaultTolerantWriteSmmDxe.h"
18 
19 EFI_HANDLE                         mHandle                   = NULL;
20 EFI_SMM_COMMUNICATION_PROTOCOL     *mSmmCommunication        = NULL;
21 UINTN                              mPrivateDataSize          = 0;
22 
23 EFI_FAULT_TOLERANT_WRITE_PROTOCOL  mFaultTolerantWriteDriver = {
24   FtwGetMaxBlockSize,
25   FtwAllocate,
26   FtwWrite,
27   FtwRestart,
28   FtwAbort,
29   FtwGetLastWrite
30 };
31 
32 /**
33   Initialize the communicate buffer using DataSize and Function number.
34 
35   @param[out]      CommunicateBuffer The communicate buffer. Caller should free it after use.
36   @param[out]      DataPtr           Points to the data in the communicate buffer. Caller should not free it.
37   @param[in]       DataSize          The payload size.
38   @param[in]       Function          The function number used to initialize the communicate header.
39 
40 **/
41 VOID
InitCommunicateBuffer(OUT VOID ** CommunicateBuffer,OUT VOID ** DataPtr,IN UINTN DataSize,IN UINTN Function)42 InitCommunicateBuffer (
43   OUT     VOID                              **CommunicateBuffer,
44   OUT     VOID                              **DataPtr,
45   IN      UINTN                             DataSize,
46   IN      UINTN                             Function
47   )
48 {
49   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
50   SMM_FTW_COMMUNICATE_FUNCTION_HEADER       *SmmFtwFunctionHeader;
51 
52   //
53   // The whole buffer size: SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE + DataSize.
54   //
55   SmmCommunicateHeader = AllocateZeroPool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE);
56   ASSERT (SmmCommunicateHeader != NULL);
57 
58   //
59   // Prepare data buffer.
60   //
61   CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gEfiSmmFaultTolerantWriteProtocolGuid);
62   SmmCommunicateHeader->MessageLength = DataSize + SMM_FTW_COMMUNICATE_HEADER_SIZE;
63 
64   SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
65   SmmFtwFunctionHeader->Function = Function;
66 
67   *CommunicateBuffer = SmmCommunicateHeader;
68   if (DataPtr != NULL) {
69     *DataPtr = SmmFtwFunctionHeader->Data;
70   }
71 }
72 
73 
74 /**
75   Send the data in communicate buffer to SMI handler and get response.
76 
77   @param[in, out]  SmmCommunicateHeader    The communicate buffer.
78   @param[in]       DataSize                The payload size.
79 
80 **/
81 EFI_STATUS
SendCommunicateBuffer(IN OUT EFI_SMM_COMMUNICATE_HEADER * SmmCommunicateHeader,IN UINTN DataSize)82 SendCommunicateBuffer (
83   IN OUT  EFI_SMM_COMMUNICATE_HEADER        *SmmCommunicateHeader,
84   IN      UINTN                             DataSize
85   )
86 {
87   EFI_STATUS                                Status;
88   UINTN                                     CommSize;
89   SMM_FTW_COMMUNICATE_FUNCTION_HEADER       *SmmFtwFunctionHeader;
90 
91   CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_FTW_COMMUNICATE_HEADER_SIZE;
92   Status = mSmmCommunication->Communicate (mSmmCommunication, SmmCommunicateHeader, &CommSize);
93   ASSERT_EFI_ERROR (Status);
94 
95   SmmFtwFunctionHeader = (SMM_FTW_COMMUNICATE_FUNCTION_HEADER *) SmmCommunicateHeader->Data;
96   return  SmmFtwFunctionHeader->ReturnStatus;
97 }
98 
99 
100 /**
101   Get the FvbBaseAddress and FvbAttributes from the FVB handle FvbHandle.
102 
103   @param[in]   FvbHandle         The handle of FVB protocol that provides services.
104   @param[out]  FvbBaseAddress    The base address of the FVB attached with FvbHandle.
105   @param[out]  FvbAttributes     The attributes of the FVB attached with FvbHandle.
106 
107   @retval EFI_SUCCESS            The function completed successfully.
108   @retval Others                 The function could not complete successfully.
109 
110 **/
111 EFI_STATUS
ConvertFvbHandle(IN EFI_HANDLE FvbHandle,OUT EFI_PHYSICAL_ADDRESS * FvbBaseAddress,OUT EFI_FVB_ATTRIBUTES_2 * FvbAttributes)112 ConvertFvbHandle (
113   IN  EFI_HANDLE                            FvbHandle,
114   OUT EFI_PHYSICAL_ADDRESS                  *FvbBaseAddress,
115   OUT EFI_FVB_ATTRIBUTES_2                  *FvbAttributes
116   )
117 {
118   EFI_STATUS                                Status;
119   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL        *Fvb;
120 
121   Status = gBS->HandleProtocol (FvbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **) &Fvb);
122   if (EFI_ERROR (Status)) {
123     return Status;
124   }
125 
126   Status = Fvb->GetPhysicalAddress (Fvb, FvbBaseAddress);
127   if (EFI_ERROR (Status)) {
128     return Status;
129   }
130 
131   Status = Fvb->GetAttributes (Fvb, FvbAttributes);
132   return Status;
133 }
134 
135 
136 /**
137   Get the size of the largest block that can be updated in a fault-tolerant manner.
138 
139   @param[in]  This             Indicates a pointer to the calling context.
140   @param[out] BlockSize        A pointer to a caller-allocated UINTN that is
141                                updated to indicate the size of the largest block
142                                that can be updated.
143 
144   @retval EFI_SUCCESS          The function completed successfully.
145   @retval EFI_ABORTED          The function could not complete successfully.
146 
147 **/
148 EFI_STATUS
149 EFIAPI
FtwGetMaxBlockSize(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,OUT UINTN * BlockSize)150 FtwGetMaxBlockSize (
151   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
152   OUT UINTN                                 *BlockSize
153   )
154 {
155   EFI_STATUS                                Status;
156   UINTN                                     PayloadSize;
157   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
158   SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER         *SmmFtwBlockSizeHeader;
159 
160   //
161   // Initialize the communicate buffer.
162   //
163   PayloadSize  = sizeof (SMM_FTW_GET_MAX_BLOCK_SIZE_HEADER);
164   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwBlockSizeHeader, PayloadSize, FTW_FUNCTION_GET_MAX_BLOCK_SIZE);
165 
166   //
167   // Send data to SMM.
168   //
169   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
170 
171   //
172   // Get data from SMM
173   //
174   *BlockSize = SmmFtwBlockSizeHeader->BlockSize;
175   FreePool (SmmCommunicateHeader);
176 
177   return Status;
178 }
179 
180 
181 /**
182   Allocates space for the protocol to maintain information about writes.
183   Since writes must be completed in a fault-tolerant manner and multiple
184   writes require more resources to be successful, this function
185   enables the protocol to ensure that enough space exists to track
186   information about upcoming writes.
187 
188   @param[in]  This             A pointer to the calling context.
189   @param[in]  CallerId         The GUID identifying the write.
190   @param[in]  PrivateDataSize  The size of the caller's private data  that must be
191                                recorded for each write.
192   @param[in]  NumberOfWrites   The number of fault tolerant block writes that will
193                                need to occur.
194 
195   @retval EFI_SUCCESS          The function completed successfully
196   @retval EFI_ABORTED          The function could not complete successfully.
197   @retval EFI_ACCESS_DENIED    Not all allocated writes have been completed.  All
198                                writes must be completed or aborted before another
199                                fault tolerant write can occur.
200 
201 **/
202 EFI_STATUS
203 EFIAPI
FtwAllocate(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,IN EFI_GUID * CallerId,IN UINTN PrivateDataSize,IN UINTN NumberOfWrites)204 FtwAllocate (
205   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
206   IN EFI_GUID                               *CallerId,
207   IN UINTN                                  PrivateDataSize,
208   IN UINTN                                  NumberOfWrites
209   )
210 {
211   EFI_STATUS                                Status;
212   UINTN                                     PayloadSize;
213   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
214   SMM_FTW_ALLOCATE_HEADER                   *SmmFtwAllocateHeader;
215 
216   //
217   // Initialize the communicate buffer.
218   //
219   PayloadSize  = sizeof (SMM_FTW_ALLOCATE_HEADER);
220   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwAllocateHeader, PayloadSize, FTW_FUNCTION_ALLOCATE);
221   CopyGuid (&SmmFtwAllocateHeader->CallerId, CallerId);
222   SmmFtwAllocateHeader->PrivateDataSize = PrivateDataSize;
223   SmmFtwAllocateHeader->NumberOfWrites  = NumberOfWrites;
224 
225   //
226   // Send data to SMM.
227   //
228   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
229   if (!EFI_ERROR( Status)) {
230     mPrivateDataSize = PrivateDataSize;
231   }
232 
233   FreePool (SmmCommunicateHeader);
234   return Status;
235 }
236 
237 
238 /**
239   Starts a target block update. This records information about the write
240   in fault tolerant storage, and will complete the write in a recoverable
241   manner, ensuring at all times that either the original contents or
242   the modified contents are available.
243 
244   @param[in]  This             The calling context.
245   @param[in]  Lba              The logical block address of the target block.
246   @param[in]  Offset           The offset within the target block to place the
247                                data.
248   @param[in]  Length           The number of bytes to write to the target block.
249   @param[in]  PrivateData      A pointer to private data that the caller requires
250                                to complete any pending writes in the event of a
251                                fault.
252   @param[in]  FvBlockHandle    The handle of FVB protocol that provides services
253                                for reading, writing, and erasing the target block.
254   @param[in]  Buffer           The data to write.
255 
256   @retval EFI_SUCCESS          The function completed successfully.
257   @retval EFI_ABORTED          The function could not complete successfully.
258   @retval EFI_BAD_BUFFER_SIZE  The write would span a block boundary, which is not
259                                a valid action.
260   @retval EFI_ACCESS_DENIED    No writes have been allocated.
261   @retval EFI_NOT_READY        The last write has not been completed. Restart()
262                                must be called to complete it.
263 
264 **/
265 EFI_STATUS
266 EFIAPI
FtwWrite(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN Length,IN VOID * PrivateData,IN EFI_HANDLE FvBlockHandle,IN VOID * Buffer)267 FtwWrite (
268   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
269   IN EFI_LBA                                Lba,
270   IN UINTN                                  Offset,
271   IN UINTN                                  Length,
272   IN VOID                                   *PrivateData,
273   IN EFI_HANDLE                             FvBlockHandle,
274   IN VOID                                   *Buffer
275   )
276 {
277   EFI_STATUS                                Status;
278   UINTN                                     PayloadSize;
279   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
280   SMM_FTW_WRITE_HEADER                      *SmmFtwWriteHeader;
281 
282   //
283   // Initialize the communicate buffer.
284   //
285   PayloadSize  = OFFSET_OF (SMM_FTW_WRITE_HEADER, Data) + Length;
286   if (PrivateData != NULL) {
287     //
288     // The private data buffer size should be the same one in FtwAllocate API.
289     //
290     PayloadSize += mPrivateDataSize;
291   }
292   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwWriteHeader, PayloadSize, FTW_FUNCTION_WRITE);
293 
294   //
295   // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
296   // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
297   //
298   Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwWriteHeader->FvbBaseAddress, &SmmFtwWriteHeader->FvbAttributes);
299   if (EFI_ERROR (Status)) {
300     FreePool (SmmCommunicateHeader);
301     return EFI_ABORTED;
302   }
303 
304   SmmFtwWriteHeader->Lba    = Lba;
305   SmmFtwWriteHeader->Offset = Offset;
306   SmmFtwWriteHeader->Length = Length;
307   CopyMem (SmmFtwWriteHeader->Data, Buffer, Length);
308   if (PrivateData == NULL) {
309     SmmFtwWriteHeader->PrivateDataSize = 0;
310   } else {
311     SmmFtwWriteHeader->PrivateDataSize = mPrivateDataSize;
312     CopyMem (&SmmFtwWriteHeader->Data[Length], PrivateData, mPrivateDataSize);
313   }
314 
315   //
316   // Send data to SMM.
317   //
318   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
319   FreePool (SmmCommunicateHeader);
320   return Status;
321 }
322 
323 
324 /**
325   Restarts a previously interrupted write. The caller must provide the
326   block protocol needed to complete the interrupted write.
327 
328   @param[in]  This             The calling context.
329   @param[in]  FvBlockHandle    The handle of FVB protocol that provides services.
330 
331   @retval EFI_SUCCESS          The function completed successfully.
332   @retval EFI_ABORTED          The function could not complete successfully.
333   @retval EFI_ACCESS_DENIED    No pending writes exist.
334 
335 **/
336 EFI_STATUS
337 EFIAPI
FtwRestart(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,IN EFI_HANDLE FvBlockHandle)338 FtwRestart (
339   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
340   IN EFI_HANDLE                             FvBlockHandle
341   )
342 {
343   EFI_STATUS                                Status;
344   UINTN                                     PayloadSize;
345   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
346   SMM_FTW_RESTART_HEADER                    *SmmFtwRestartHeader;
347 
348   //
349   // Initialize the communicate buffer.
350   //
351   PayloadSize  = sizeof (SMM_FTW_RESTART_HEADER);
352   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwRestartHeader, PayloadSize, FTW_FUNCTION_RESTART);
353 
354   //
355   // FvBlockHandle can not be used in SMM environment. Here we get the FVB protocol first, then get FVB base address
356   // and its attribute. Send these information to SMM handler, the SMM handler will find the proper FVB to write data.
357   //
358   Status = ConvertFvbHandle (FvBlockHandle, &SmmFtwRestartHeader->FvbBaseAddress, &SmmFtwRestartHeader->FvbAttributes);
359   if (EFI_ERROR (Status)) {
360     FreePool (SmmCommunicateHeader);
361     return EFI_ABORTED;
362   }
363 
364   //
365   // Send data to SMM.
366   //
367   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
368   FreePool (SmmCommunicateHeader);
369   return Status;
370 }
371 
372 
373 /**
374   Aborts all previously allocated writes.
375 
376   @param[in]  This             The calling context.
377 
378   @retval EFI_SUCCESS          The function completed successfully.
379   @retval EFI_ABORTED          The function could not complete successfully.
380   @retval EFI_NOT_FOUND        No allocated writes exist.
381 
382 **/
383 EFI_STATUS
384 EFIAPI
FtwAbort(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This)385 FtwAbort (
386   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This
387   )
388 {
389   EFI_STATUS                                Status;
390   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
391 
392   //
393   // Initialize the communicate buffer.
394   //
395   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, NULL, 0, FTW_FUNCTION_ABORT);
396 
397   //
398   // Send data to SMM.
399   //
400   Status = SendCommunicateBuffer (SmmCommunicateHeader, 0);
401 
402   FreePool (SmmCommunicateHeader);
403   return Status;
404 }
405 
406 
407 /**
408   Starts a target block update. This function records information about the write
409   in fault-tolerant storage and completes the write in a recoverable
410   manner, ensuring at all times that either the original contents or
411   the modified contents are available.
412 
413   @param[in]      This            Indicates a pointer to the calling context.
414   @param[out]     CallerId        The GUID identifying the last write.
415   @param[out]     Lba             The logical block address of the last write.
416   @param[out]     Offset          The offset within the block of the last write.
417   @param[out]     Length          The length of the last write.
418   @param[in, out] PrivateDataSize On input, the size of the PrivateData buffer. On
419                                   output, the size of the private data stored for
420                                   this write.
421   @param[out]     PrivateData     A pointer to a buffer. The function will copy
422                                   PrivateDataSize bytes from the private data stored
423                                   for this write.
424   @param[out]     Complete        A Boolean value with TRUE indicating that the write
425                                   was completed.
426 
427   @retval EFI_SUCCESS             The function completed successfully.
428   @retval EFI_ABORTED             The function could not complete successfully.
429   @retval EFI_NOT_FOUND           No allocated writes exist.
430 
431 **/
432 EFI_STATUS
433 EFIAPI
FtwGetLastWrite(IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL * This,OUT EFI_GUID * CallerId,OUT EFI_LBA * Lba,OUT UINTN * Offset,OUT UINTN * Length,IN OUT UINTN * PrivateDataSize,OUT VOID * PrivateData,OUT BOOLEAN * Complete)434 FtwGetLastWrite (
435   IN EFI_FAULT_TOLERANT_WRITE_PROTOCOL      *This,
436   OUT EFI_GUID                              *CallerId,
437   OUT EFI_LBA                               *Lba,
438   OUT UINTN                                 *Offset,
439   OUT UINTN                                 *Length,
440   IN OUT UINTN                              *PrivateDataSize,
441   OUT VOID                                  *PrivateData,
442   OUT BOOLEAN                               *Complete
443   )
444 {
445   EFI_STATUS                                Status;
446   UINTN                                     PayloadSize;
447   EFI_SMM_COMMUNICATE_HEADER                *SmmCommunicateHeader;
448   SMM_FTW_GET_LAST_WRITE_HEADER             *SmmFtwGetLastWriteHeader;
449 
450   //
451   // Initialize the communicate buffer.
452   //
453   PayloadSize  = OFFSET_OF (SMM_FTW_GET_LAST_WRITE_HEADER, Data) + *PrivateDataSize;
454   InitCommunicateBuffer ((VOID **)&SmmCommunicateHeader, (VOID **)&SmmFtwGetLastWriteHeader, PayloadSize, FTW_FUNCTION_GET_LAST_WRITE);
455   SmmFtwGetLastWriteHeader->PrivateDataSize = *PrivateDataSize;
456 
457   //
458   // Send data to SMM.
459   //
460   Status = SendCommunicateBuffer (SmmCommunicateHeader, PayloadSize);
461 
462   //
463   // Get data from SMM
464   //
465   *PrivateDataSize = SmmFtwGetLastWriteHeader->PrivateDataSize;
466   if (Status == EFI_SUCCESS || Status == EFI_BUFFER_TOO_SMALL) {
467     *Lba      = SmmFtwGetLastWriteHeader->Lba;
468     *Offset   = SmmFtwGetLastWriteHeader->Offset;
469     *Length   = SmmFtwGetLastWriteHeader->Length;
470     *Complete = SmmFtwGetLastWriteHeader->Complete;
471     CopyGuid (CallerId, &SmmFtwGetLastWriteHeader->CallerId);
472     if (Status == EFI_SUCCESS) {
473       CopyMem (PrivateData, SmmFtwGetLastWriteHeader->Data, *PrivateDataSize);
474     }
475   } else if (Status == EFI_NOT_FOUND) {
476     *Complete = SmmFtwGetLastWriteHeader->Complete;
477   }
478 
479   FreePool (SmmCommunicateHeader);
480   return Status;
481 }
482 
483 /**
484   SMM Fault Tolerant Write Protocol notification event handler.
485 
486   Install Fault Tolerant Write Protocol.
487 
488   @param[in] Event    Event whose notification function is being invoked.
489   @param[in] Context  Pointer to the notification function's context.
490 **/
491 VOID
492 EFIAPI
SmmFtwReady(IN EFI_EVENT Event,IN VOID * Context)493 SmmFtwReady (
494   IN  EFI_EVENT                             Event,
495   IN  VOID                                  *Context
496   )
497 {
498   EFI_STATUS                                Status;
499   EFI_FAULT_TOLERANT_WRITE_PROTOCOL         *FtwProtocol;
500 
501   //
502   // Just return to avoid install SMM FaultTolerantWriteProtocol again
503   // if Fault Tolerant Write protocol had been installed.
504   //
505   Status = gBS->LocateProtocol (&gEfiFaultTolerantWriteProtocolGuid, NULL, (VOID **)&FtwProtocol);
506   if (!EFI_ERROR (Status)) {
507     return;
508   }
509 
510   Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication);
511   ASSERT_EFI_ERROR (Status);
512 
513   //
514   // Install protocol interface
515   //
516   Status = gBS->InstallProtocolInterface (
517                   &mHandle,
518                   &gEfiFaultTolerantWriteProtocolGuid,
519                   EFI_NATIVE_INTERFACE,
520                   &mFaultTolerantWriteDriver
521                   );
522   ASSERT_EFI_ERROR (Status);
523 
524   Status = gBS->CloseEvent (Event);
525   ASSERT_EFI_ERROR (Status);
526 }
527 
528 
529 /**
530   The driver entry point for Fault Tolerant Write driver.
531 
532   The function does the necessary initialization work.
533 
534   @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
535   @param[in]  SystemTable       A pointer to the EFI system table.
536 
537   @retval     EFI_SUCCESS       This funtion always return EFI_SUCCESS.
538 
539 **/
540 EFI_STATUS
541 EFIAPI
FaultTolerantWriteSmmInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)542 FaultTolerantWriteSmmInitialize (
543   IN EFI_HANDLE                             ImageHandle,
544   IN EFI_SYSTEM_TABLE                       *SystemTable
545   )
546 {
547   VOID                                      *SmmFtwRegistration;
548 
549   //
550   // Smm FTW driver is ready
551   //
552   EfiCreateProtocolNotifyEvent (
553     &gEfiSmmFaultTolerantWriteProtocolGuid,
554     TPL_CALLBACK,
555     SmmFtwReady,
556     NULL,
557     &SmmFtwRegistration
558     );
559 
560   return EFI_SUCCESS;
561 }
562 
563