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 | }
|
---|