1/** @file
2
3  Implement the Fault Tolerant Write (FTW) protocol based on SMM FTW
4  module.
5
6Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved. <BR>
7This program and the accompanying materials
8are licensed and made available under the terms and conditions of the BSD License
9which accompanies this distribution.  The full text of the license may be found at
10http://opensource.org/licenses/bsd-license.php
11
12THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15**/
16
17#include "FaultTolerantWriteSmmDxe.h"
18
19EFI_HANDLE                         mHandle                   = NULL;
20EFI_SMM_COMMUNICATION_PROTOCOL     *mSmmCommunication        = NULL;
21UINTN                              mPrivateDataSize          = 0;
22
23EFI_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**/
41VOID
42InitCommunicateBuffer (
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**/
81EFI_STATUS
82SendCommunicateBuffer (
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**/
111EFI_STATUS
112ConvertFvbHandle (
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**/
148EFI_STATUS
149EFIAPI
150FtwGetMaxBlockSize (
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**/
202EFI_STATUS
203EFIAPI
204FtwAllocate (
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**/
265EFI_STATUS
266EFIAPI
267FtwWrite (
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**/
336EFI_STATUS
337EFIAPI
338FtwRestart (
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**/
383EFI_STATUS
384EFIAPI
385FtwAbort (
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**/
432EFI_STATUS
433EFIAPI
434FtwGetLastWrite (
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**/
491VOID
492EFIAPI
493SmmFtwReady (
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**/
540EFI_STATUS
541EFIAPI
542FaultTolerantWriteSmmInitialize (
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