]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 - 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 }