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