]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c
MdeModulePkg FaultTolerantWritePei: Refine the code to avoid error report.
[mirror_edk2.git] / MdeModulePkg / Universal / FaultTolerantWritePei / FaultTolerantWritePei.c
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
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
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
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
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 }