• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   This driver installs gEdkiiFaultTolerantWriteGuid PPI to inform
3   the check for FTW last write data has been done.
4 
5 Copyright (c) 2013, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution.  The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10 
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <PiPei.h>
17 
18 #include <Guid/SystemNvDataGuid.h>
19 #include <Guid/FaultTolerantWrite.h>
20 #include <Library/PeiServicesLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/HobLib.h>
25 
26 EFI_PEI_PPI_DESCRIPTOR     mPpiListVariable = {
27   (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
28   &gEdkiiFaultTolerantWriteGuid,
29   NULL
30 };
31 
32 /**
33   Get the last Write Header pointer.
34   The last write header is the header whose 'complete' state hasn't been set.
35   After all, this header may be a EMPTY header entry for next Allocate.
36 
37 
38   @param FtwWorkSpaceHeader Pointer of the working block header
39   @param FtwWorkSpaceSize   Size of the work space
40   @param FtwWriteHeader     Pointer to retrieve the last write header
41 
42   @retval  EFI_SUCCESS      Get the last write record successfully
43   @retval  EFI_ABORTED      The FTW work space is damaged
44 
45 **/
46 EFI_STATUS
FtwGetLastWriteHeader(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * FtwWorkSpaceHeader,IN UINTN FtwWorkSpaceSize,OUT EFI_FAULT_TOLERANT_WRITE_HEADER ** FtwWriteHeader)47 FtwGetLastWriteHeader (
48   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER  *FtwWorkSpaceHeader,
49   IN UINTN                                    FtwWorkSpaceSize,
50   OUT EFI_FAULT_TOLERANT_WRITE_HEADER         **FtwWriteHeader
51   )
52 {
53   UINTN                           Offset;
54   EFI_FAULT_TOLERANT_WRITE_HEADER *FtwHeader;
55 
56   *FtwWriteHeader = NULL;
57   FtwHeader       = (EFI_FAULT_TOLERANT_WRITE_HEADER *) (FtwWorkSpaceHeader + 1);
58   Offset          = sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER);
59 
60   while (FtwHeader->Complete == FTW_VALID_STATE) {
61     Offset += FTW_WRITE_TOTAL_SIZE (FtwHeader->NumberOfWrites, FtwHeader->PrivateDataSize);
62     //
63     // If Offset exceed the FTW work space boudary, return error.
64     //
65     if (Offset >= FtwWorkSpaceSize) {
66       *FtwWriteHeader = FtwHeader;
67       return EFI_ABORTED;
68     }
69 
70     FtwHeader = (EFI_FAULT_TOLERANT_WRITE_HEADER *) ((UINT8 *) FtwWorkSpaceHeader + Offset);
71   }
72   //
73   // Last write header is found
74   //
75   *FtwWriteHeader = FtwHeader;
76 
77   return EFI_SUCCESS;
78 }
79 
80 /**
81   Get the last Write Record pointer. The last write Record is the Record
82   whose DestinationCompleted state hasn't been set. After all, this Record
83   may be a EMPTY record entry for next write.
84 
85 
86   @param FtwWriteHeader  Pointer to the write record header
87   @param FtwWriteRecord  Pointer to retrieve the last write record
88 
89   @retval EFI_SUCCESS        Get the last write record successfully
90   @retval EFI_ABORTED        The FTW work space is damaged
91 
92 **/
93 EFI_STATUS
FtwGetLastWriteRecord(IN EFI_FAULT_TOLERANT_WRITE_HEADER * FtwWriteHeader,OUT EFI_FAULT_TOLERANT_WRITE_RECORD ** FtwWriteRecord)94 FtwGetLastWriteRecord (
95   IN EFI_FAULT_TOLERANT_WRITE_HEADER          *FtwWriteHeader,
96   OUT EFI_FAULT_TOLERANT_WRITE_RECORD         **FtwWriteRecord
97   )
98 {
99   UINTN                           Index;
100   EFI_FAULT_TOLERANT_WRITE_RECORD *FtwRecord;
101 
102   *FtwWriteRecord = NULL;
103   FtwRecord       = (EFI_FAULT_TOLERANT_WRITE_RECORD *) (FtwWriteHeader + 1);
104 
105   //
106   // Try to find the last write record "that has not completed"
107   //
108   for (Index = 0; Index < FtwWriteHeader->NumberOfWrites; Index += 1) {
109     if (FtwRecord->DestinationComplete != FTW_VALID_STATE) {
110       //
111       // The last write record is found
112       //
113       *FtwWriteRecord = FtwRecord;
114       return EFI_SUCCESS;
115     }
116 
117     FtwRecord++;
118 
119     if (FtwWriteHeader->PrivateDataSize != 0) {
120       FtwRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord + (UINTN) FtwWriteHeader->PrivateDataSize);
121     }
122   }
123   //
124   //  if Index == NumberOfWrites, then
125   //  the last record has been written successfully,
126   //  but the Header->Complete Flag has not been set.
127   //  also return the last record.
128   //
129   if (Index == FtwWriteHeader->NumberOfWrites) {
130     *FtwWriteRecord = (EFI_FAULT_TOLERANT_WRITE_RECORD *) ((UINTN) FtwRecord - FTW_RECORD_SIZE (FtwWriteHeader->PrivateDataSize));
131     return EFI_SUCCESS;
132   }
133 
134   return EFI_ABORTED;
135 }
136 
137 /**
138   Check to see if it is a valid work space.
139 
140 
141   @param WorkingHeader   Pointer of working block header
142   @param WorkingLength   Working block length
143 
144   @retval TRUE          The work space is valid.
145   @retval FALSE         The work space is invalid.
146 
147 **/
148 BOOLEAN
IsValidWorkSpace(IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER * WorkingHeader,IN UINTN WorkingLength)149 IsValidWorkSpace (
150   IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER    *WorkingHeader,
151   IN UINTN                                      WorkingLength
152   )
153 {
154   UINT8 Data;
155 
156   if (WorkingHeader == NULL) {
157     return FALSE;
158   }
159 
160   if ((WorkingHeader->WorkingBlockValid != FTW_VALID_STATE) || (WorkingHeader->WorkingBlockInvalid == FTW_VALID_STATE)) {
161     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header valid bit check error\n"));
162     return FALSE;
163   }
164 
165   if (WorkingHeader->WriteQueueSize != (WorkingLength - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER))) {
166     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header WriteQueueSize check error\n"));
167     return FALSE;
168   }
169 
170   //
171   // Check signature with gEdkiiWorkingBlockSignatureGuid
172   //
173   if (!CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &WorkingHeader->Signature)) {
174     DEBUG ((EFI_D_ERROR, "FtwPei: Work block header signature check error, it should be gEdkiiWorkingBlockSignatureGuid\n"));
175     //
176     // To be compatible with old signature gEfiSystemNvDataFvGuid.
177     //
178     if (!CompareGuid (&gEfiSystemNvDataFvGuid, &WorkingHeader->Signature)) {
179       return FALSE;
180     } else {
181       Data = *(UINT8 *) (WorkingHeader + 1);
182       if (Data != 0xff) {
183         DEBUG ((EFI_D_ERROR, "FtwPei: Old format FTW structure can't be handled\n"));
184         ASSERT (FALSE);
185         return FALSE;
186       }
187     }
188   }
189 
190   return TRUE;
191 
192 }
193 
194 /**
195   Main entry for Fault Tolerant Write PEIM.
196 
197   @param[in]  FileHandle              Handle of the file being invoked.
198   @param[in]  PeiServices             Pointer to PEI Services table.
199 
200   @retval EFI_SUCCESS  If the interface could be successfully installed
201   @retval Others       Returned from PeiServicesInstallPpi()
202 
203 **/
204 EFI_STATUS
205 EFIAPI
PeimFaultTolerantWriteInitialize(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)206 PeimFaultTolerantWriteInitialize (
207   IN       EFI_PEI_FILE_HANDLE  FileHandle,
208   IN CONST EFI_PEI_SERVICES     **PeiServices
209   )
210 {
211   EFI_STATUS                                Status;
212   EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER   *FtwWorkingBlockHeader;
213   EFI_FAULT_TOLERANT_WRITE_HEADER           *FtwLastWriteHeader;
214   EFI_FAULT_TOLERANT_WRITE_RECORD           *FtwLastWriteRecord;
215   EFI_PHYSICAL_ADDRESS                      WorkSpaceAddress;
216   UINTN                                     WorkSpaceLength;
217   EFI_PHYSICAL_ADDRESS                      SpareAreaAddress;
218   UINTN                                     SpareAreaLength;
219   EFI_PHYSICAL_ADDRESS                      WorkSpaceInSpareArea;
220   FAULT_TOLERANT_WRITE_LAST_WRITE_DATA      FtwLastWrite;
221 
222   FtwWorkingBlockHeader = NULL;
223   FtwLastWriteHeader = NULL;
224   FtwLastWriteRecord = NULL;
225 
226   WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwWorkingBase64);
227   if (WorkSpaceAddress == 0) {
228     WorkSpaceAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwWorkingBase);
229   }
230   WorkSpaceLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwWorkingSize);
231 
232   SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdFlashNvStorageFtwSpareBase64);
233   if (SpareAreaAddress == 0) {
234     SpareAreaAddress = (EFI_PHYSICAL_ADDRESS) PcdGet32 (PcdFlashNvStorageFtwSpareBase);
235   }
236   SpareAreaLength = (UINTN) PcdGet32 (PcdFlashNvStorageFtwSpareSize);
237 
238   //
239   // The address of FTW working base and spare base must not be 0.
240   //
241   ASSERT ((WorkSpaceAddress != 0) && (SpareAreaAddress != 0));
242 
243   FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceAddress;
244   if (IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
245     Status = FtwGetLastWriteHeader (
246                FtwWorkingBlockHeader,
247                WorkSpaceLength,
248                &FtwLastWriteHeader
249                );
250     if (!EFI_ERROR (Status)) {
251       Status = FtwGetLastWriteRecord (
252                  FtwLastWriteHeader,
253                  &FtwLastWriteRecord
254                  );
255     }
256 
257     if (!EFI_ERROR (Status)) {
258       ASSERT (FtwLastWriteRecord != NULL);
259       if ((FtwLastWriteRecord->SpareComplete == FTW_VALID_STATE) && (FtwLastWriteRecord->DestinationComplete != FTW_VALID_STATE)) {
260         //
261         // If FTW last write was still in progress with SpareComplete set and DestinationComplete not set.
262         // It means the target buffer has been backed up in spare block, then target block has been erased,
263         // but the target buffer has not been writen in target block from spare block, we need to build
264         // FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob to hold the FTW last write data.
265         //
266         FtwLastWrite.TargetAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) ((INT64) SpareAreaAddress + FtwLastWriteRecord->RelativeOffset);
267         FtwLastWrite.SpareAddress = SpareAreaAddress;
268         FtwLastWrite.Length = SpareAreaLength;
269         DEBUG ((
270           EFI_D_INFO,
271           "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
272           (UINTN) FtwLastWrite.TargetAddress,
273           (UINTN) FtwLastWrite.SpareAddress,
274           (UINTN) FtwLastWrite.Length));
275         BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
276       }
277     }
278   } else {
279     FtwWorkingBlockHeader = NULL;
280     //
281     // If the working block workspace is not valid, try to find workspace in the spare block.
282     //
283     WorkSpaceInSpareArea = SpareAreaAddress + SpareAreaLength - WorkSpaceLength;
284     while (WorkSpaceInSpareArea >= SpareAreaAddress) {
285       if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, (EFI_GUID *) (UINTN) WorkSpaceInSpareArea)) {
286         //
287         // Found the workspace.
288         //
289         DEBUG ((EFI_D_INFO, "FtwPei: workspace in spare block is at 0x%x.\n", (UINTN) WorkSpaceInSpareArea));
290         FtwWorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (UINTN) WorkSpaceInSpareArea;
291         break;
292       }
293       WorkSpaceInSpareArea = WorkSpaceInSpareArea - sizeof (EFI_GUID);
294     }
295     if ((FtwWorkingBlockHeader != NULL) && IsValidWorkSpace (FtwWorkingBlockHeader, WorkSpaceLength)) {
296       //
297       // It was workspace self reclaim, build FAULT_TOLERANT_WRITE_LAST_WRITE_DATA GUID hob for it.
298       //
299       FtwLastWrite.TargetAddress = WorkSpaceAddress - (WorkSpaceInSpareArea - SpareAreaAddress);
300       FtwLastWrite.SpareAddress = SpareAreaAddress;
301       FtwLastWrite.Length = SpareAreaLength;
302       DEBUG ((
303         EFI_D_INFO,
304         "FtwPei last write data: TargetAddress - 0x%x SpareAddress - 0x%x Length - 0x%x\n",
305         (UINTN) FtwLastWrite.TargetAddress,
306         (UINTN) FtwLastWrite.SpareAddress,
307         (UINTN) FtwLastWrite.Length));
308       BuildGuidDataHob (&gEdkiiFaultTolerantWriteGuid, (VOID *) &FtwLastWrite, sizeof (FAULT_TOLERANT_WRITE_LAST_WRITE_DATA));
309     } else {
310       //
311       // Both are invalid.
312       //
313       DEBUG ((EFI_D_ERROR, "FtwPei: Both working and spare block are invalid.\n"));
314     }
315   }
316 
317   //
318   // Install gEdkiiFaultTolerantWriteGuid PPI to inform the check for FTW last write data has been done.
319   //
320   return PeiServicesInstallPpi (&mPpiListVariable);
321 }
322