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