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