1 /** @file 2 3 Internal functions to operate Working Block Space. 4 5 Copyright (c) 2006 - 2015, 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 17 #include "FaultTolerantWrite.h" 18 19 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER mWorkingBlockHeader = {ZERO_GUID, 0, 0, 0, 0, {0, 0, 0}, 0}; 20 21 /** 22 Initialize a local work space header. 23 24 Since Signature and WriteQueueSize have been known, Crc can be calculated out, 25 then the work space header will be fixed. 26 **/ 27 VOID 28 InitializeLocalWorkSpaceHeader ( 29 VOID 30 ) 31 { 32 EFI_STATUS Status; 33 34 // 35 // Check signature with gEdkiiWorkingBlockSignatureGuid. 36 // 37 if (CompareGuid (&gEdkiiWorkingBlockSignatureGuid, &mWorkingBlockHeader.Signature)) { 38 // 39 // The local work space header has been initialized. 40 // 41 return; 42 } 43 44 SetMem ( 45 &mWorkingBlockHeader, 46 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), 47 FTW_ERASED_BYTE 48 ); 49 50 // 51 // Here using gEdkiiWorkingBlockSignatureGuid as the signature. 52 // 53 CopyMem ( 54 &mWorkingBlockHeader.Signature, 55 &gEdkiiWorkingBlockSignatureGuid, 56 sizeof (EFI_GUID) 57 ); 58 mWorkingBlockHeader.WriteQueueSize = (UINT64) (PcdGet32 (PcdFlashNvStorageFtwWorkingSize) - sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); 59 60 // 61 // Crc is calculated with all the fields except Crc and STATE, so leave them as FTW_ERASED_BYTE. 62 // 63 64 // 65 // Calculate the Crc of woking block header 66 // 67 Status = gBS->CalculateCrc32 ( 68 &mWorkingBlockHeader, 69 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), 70 &mWorkingBlockHeader.Crc 71 ); 72 ASSERT_EFI_ERROR (Status); 73 74 mWorkingBlockHeader.WorkingBlockValid = FTW_VALID_STATE; 75 mWorkingBlockHeader.WorkingBlockInvalid = FTW_INVALID_STATE; 76 } 77 78 /** 79 Check to see if it is a valid work space. 80 81 82 @param WorkingHeader Pointer of working block header 83 84 @retval TRUE The work space is valid. 85 @retval FALSE The work space is invalid. 86 87 **/ 88 BOOLEAN 89 IsValidWorkSpace ( 90 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader 91 ) 92 { 93 if (WorkingHeader == NULL) { 94 return FALSE; 95 } 96 97 if (CompareMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)) == 0) { 98 return TRUE; 99 } 100 101 DEBUG ((EFI_D_INFO, "Ftw: Work block header check mismatch\n")); 102 return FALSE; 103 } 104 105 /** 106 Initialize a work space when there is no work space. 107 108 @param WorkingHeader Pointer of working block header 109 110 @retval EFI_SUCCESS The function completed successfully 111 @retval EFI_ABORTED The function could not complete successfully. 112 113 **/ 114 EFI_STATUS 115 InitWorkSpaceHeader ( 116 IN EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingHeader 117 ) 118 { 119 if (WorkingHeader == NULL) { 120 return EFI_INVALID_PARAMETER; 121 } 122 123 CopyMem (WorkingHeader, &mWorkingBlockHeader, sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER)); 124 125 return EFI_SUCCESS; 126 } 127 128 /** 129 Read work space data from work block or spare block. 130 131 @param FvBlock FVB Protocol interface to access the block. 132 @param BlockSize The size of the block. 133 @param Lba Lba of the block. 134 @param Offset The offset within the block. 135 @param Length The number of bytes to read from the block. 136 @param Buffer The data is read. 137 138 @retval EFI_SUCCESS The function completed successfully. 139 @retval EFI_ABORTED The function could not complete successfully. 140 141 **/ 142 EFI_STATUS 143 ReadWorkSpaceData ( 144 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, 145 IN UINTN BlockSize, 146 IN EFI_LBA Lba, 147 IN UINTN Offset, 148 IN UINTN Length, 149 OUT UINT8 *Buffer 150 ) 151 { 152 EFI_STATUS Status; 153 UINT8 *Ptr; 154 UINTN MyLength; 155 156 // 157 // Calculate the real Offset and Lba to write. 158 // 159 while (Offset >= BlockSize) { 160 Offset -= BlockSize; 161 Lba++; 162 } 163 164 Ptr = Buffer; 165 while (Length > 0) { 166 if ((Offset + Length) > BlockSize) { 167 MyLength = BlockSize - Offset; 168 } else { 169 MyLength = Length; 170 } 171 172 Status = FvBlock->Read ( 173 FvBlock, 174 Lba, 175 Offset, 176 &MyLength, 177 Ptr 178 ); 179 if (EFI_ERROR (Status)) { 180 return EFI_ABORTED; 181 } 182 Offset = 0; 183 Length -= MyLength; 184 Ptr += MyLength; 185 Lba++; 186 } 187 188 return EFI_SUCCESS; 189 } 190 191 /** 192 Write work space data to work block. 193 194 @param FvBlock FVB Protocol interface to access the block. 195 @param BlockSize The size of the block. 196 @param Lba Lba of the block. 197 @param Offset The offset within the block to place the data. 198 @param Length The number of bytes to write to the block. 199 @param Buffer The data to write. 200 201 @retval EFI_SUCCESS The function completed successfully. 202 @retval EFI_ABORTED The function could not complete successfully. 203 204 **/ 205 EFI_STATUS 206 WriteWorkSpaceData ( 207 IN EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *FvBlock, 208 IN UINTN BlockSize, 209 IN EFI_LBA Lba, 210 IN UINTN Offset, 211 IN UINTN Length, 212 IN UINT8 *Buffer 213 ) 214 { 215 EFI_STATUS Status; 216 UINT8 *Ptr; 217 UINTN MyLength; 218 219 // 220 // Calculate the real Offset and Lba to write. 221 // 222 while (Offset >= BlockSize) { 223 Offset -= BlockSize; 224 Lba++; 225 } 226 227 Ptr = Buffer; 228 while (Length > 0) { 229 if ((Offset + Length) > BlockSize) { 230 MyLength = BlockSize - Offset; 231 } else { 232 MyLength = Length; 233 } 234 235 Status = FvBlock->Write ( 236 FvBlock, 237 Lba, 238 Offset, 239 &MyLength, 240 Ptr 241 ); 242 if (EFI_ERROR (Status)) { 243 return EFI_ABORTED; 244 } 245 Offset = 0; 246 Length -= MyLength; 247 Ptr += MyLength; 248 Lba++; 249 } 250 return EFI_SUCCESS; 251 } 252 253 /** 254 Read from working block to refresh the work space in memory. 255 256 @param FtwDevice Point to private data of FTW driver 257 258 @retval EFI_SUCCESS The function completed successfully 259 @retval EFI_ABORTED The function could not complete successfully. 260 261 **/ 262 EFI_STATUS 263 WorkSpaceRefresh ( 264 IN EFI_FTW_DEVICE *FtwDevice 265 ) 266 { 267 EFI_STATUS Status; 268 UINTN RemainingSpaceSize; 269 270 // 271 // Initialize WorkSpace as FTW_ERASED_BYTE 272 // 273 SetMem ( 274 FtwDevice->FtwWorkSpace, 275 FtwDevice->FtwWorkSpaceSize, 276 FTW_ERASED_BYTE 277 ); 278 279 // 280 // Read from working block 281 // 282 Status = ReadWorkSpaceData ( 283 FtwDevice->FtwFvBlock, 284 FtwDevice->WorkBlockSize, 285 FtwDevice->FtwWorkSpaceLba, 286 FtwDevice->FtwWorkSpaceBase, 287 FtwDevice->FtwWorkSpaceSize, 288 FtwDevice->FtwWorkSpace 289 ); 290 if (EFI_ERROR (Status)) { 291 return EFI_ABORTED; 292 } 293 // 294 // Refresh the FtwLastWriteHeader 295 // 296 Status = FtwGetLastWriteHeader ( 297 FtwDevice->FtwWorkSpaceHeader, 298 FtwDevice->FtwWorkSpaceSize, 299 &FtwDevice->FtwLastWriteHeader 300 ); 301 RemainingSpaceSize = FtwDevice->FtwWorkSpaceSize - ((UINTN) FtwDevice->FtwLastWriteHeader - (UINTN) FtwDevice->FtwWorkSpace); 302 DEBUG ((EFI_D_INFO, "Ftw: Remaining work space size - %x\n", RemainingSpaceSize)); 303 // 304 // If FtwGetLastWriteHeader() returns error, or the remaining space size is even not enough to contain 305 // one EFI_FAULT_TOLERANT_WRITE_HEADER + one EFI_FAULT_TOLERANT_WRITE_RECORD(It will cause that the header 306 // pointed by FtwDevice->FtwLastWriteHeader or record pointed by FtwDevice->FtwLastWriteRecord may contain invalid data), 307 // it needs to reclaim work space. 308 // 309 if (EFI_ERROR (Status) || RemainingSpaceSize < sizeof (EFI_FAULT_TOLERANT_WRITE_HEADER) + sizeof (EFI_FAULT_TOLERANT_WRITE_RECORD)) { 310 // 311 // reclaim work space in working block. 312 // 313 Status = FtwReclaimWorkSpace (FtwDevice, TRUE); 314 if (EFI_ERROR (Status)) { 315 DEBUG ((EFI_D_ERROR, "Ftw: Reclaim workspace - %r\n", Status)); 316 return EFI_ABORTED; 317 } 318 // 319 // Read from working block again 320 // 321 Status = ReadWorkSpaceData ( 322 FtwDevice->FtwFvBlock, 323 FtwDevice->WorkBlockSize, 324 FtwDevice->FtwWorkSpaceLba, 325 FtwDevice->FtwWorkSpaceBase, 326 FtwDevice->FtwWorkSpaceSize, 327 FtwDevice->FtwWorkSpace 328 ); 329 if (EFI_ERROR (Status)) { 330 return EFI_ABORTED; 331 } 332 333 Status = FtwGetLastWriteHeader ( 334 FtwDevice->FtwWorkSpaceHeader, 335 FtwDevice->FtwWorkSpaceSize, 336 &FtwDevice->FtwLastWriteHeader 337 ); 338 if (EFI_ERROR (Status)) { 339 return EFI_ABORTED; 340 } 341 } 342 // 343 // Refresh the FtwLastWriteRecord 344 // 345 Status = FtwGetLastWriteRecord ( 346 FtwDevice->FtwLastWriteHeader, 347 &FtwDevice->FtwLastWriteRecord 348 ); 349 if (EFI_ERROR (Status)) { 350 return EFI_ABORTED; 351 } 352 353 return EFI_SUCCESS; 354 } 355 356 /** 357 Reclaim the work space on the working block. 358 359 @param FtwDevice Point to private data of FTW driver 360 @param PreserveRecord Whether to preserve the working record is needed 361 362 @retval EFI_SUCCESS The function completed successfully 363 @retval EFI_OUT_OF_RESOURCES Allocate memory error 364 @retval EFI_ABORTED The function could not complete successfully 365 366 **/ 367 EFI_STATUS 368 FtwReclaimWorkSpace ( 369 IN EFI_FTW_DEVICE *FtwDevice, 370 IN BOOLEAN PreserveRecord 371 ) 372 { 373 EFI_STATUS Status; 374 UINTN Length; 375 EFI_FAULT_TOLERANT_WRITE_HEADER *Header; 376 UINT8 *TempBuffer; 377 UINTN TempBufferSize; 378 UINTN SpareBufferSize; 379 UINT8 *SpareBuffer; 380 EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *WorkingBlockHeader; 381 UINTN Index; 382 UINT8 *Ptr; 383 EFI_LBA WorkSpaceLbaOffset; 384 385 DEBUG ((EFI_D_INFO, "Ftw: start to reclaim work space\n")); 386 387 WorkSpaceLbaOffset = FtwDevice->FtwWorkSpaceLba - FtwDevice->FtwWorkBlockLba; 388 389 // 390 // Read all original data from working block to a memory buffer 391 // 392 TempBufferSize = FtwDevice->NumberOfWorkBlock * FtwDevice->WorkBlockSize; 393 TempBuffer = AllocateZeroPool (TempBufferSize); 394 if (TempBuffer == NULL) { 395 return EFI_OUT_OF_RESOURCES; 396 } 397 398 Ptr = TempBuffer; 399 for (Index = 0; Index < FtwDevice->NumberOfWorkBlock; Index += 1) { 400 Length = FtwDevice->WorkBlockSize; 401 Status = FtwDevice->FtwFvBlock->Read ( 402 FtwDevice->FtwFvBlock, 403 FtwDevice->FtwWorkBlockLba + Index, 404 0, 405 &Length, 406 Ptr 407 ); 408 if (EFI_ERROR (Status)) { 409 FreePool (TempBuffer); 410 return EFI_ABORTED; 411 } 412 413 Ptr += Length; 414 } 415 // 416 // Clean up the workspace, remove all the completed records. 417 // 418 Ptr = TempBuffer + 419 (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + 420 FtwDevice->FtwWorkSpaceBase; 421 422 // 423 // Clear the content of buffer that will save the new work space data 424 // 425 SetMem (Ptr, FtwDevice->FtwWorkSpaceSize, FTW_ERASED_BYTE); 426 427 // 428 // Copy EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER to buffer 429 // 430 CopyMem ( 431 Ptr, 432 FtwDevice->FtwWorkSpaceHeader, 433 sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER) 434 ); 435 if (PreserveRecord) { 436 // 437 // Get the last record following the header, 438 // 439 Status = FtwGetLastWriteHeader ( 440 FtwDevice->FtwWorkSpaceHeader, 441 FtwDevice->FtwWorkSpaceSize, 442 &FtwDevice->FtwLastWriteHeader 443 ); 444 Header = FtwDevice->FtwLastWriteHeader; 445 if (!EFI_ERROR (Status) && (Header != NULL) && (Header->Complete != FTW_VALID_STATE) && (Header->HeaderAllocated == FTW_VALID_STATE)) { 446 CopyMem ( 447 Ptr + sizeof (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER), 448 FtwDevice->FtwLastWriteHeader, 449 FTW_WRITE_TOTAL_SIZE (Header->NumberOfWrites, Header->PrivateDataSize) 450 ); 451 } 452 } 453 454 CopyMem ( 455 FtwDevice->FtwWorkSpace, 456 Ptr, 457 FtwDevice->FtwWorkSpaceSize 458 ); 459 460 FtwGetLastWriteHeader ( 461 FtwDevice->FtwWorkSpaceHeader, 462 FtwDevice->FtwWorkSpaceSize, 463 &FtwDevice->FtwLastWriteHeader 464 ); 465 466 FtwGetLastWriteRecord ( 467 FtwDevice->FtwLastWriteHeader, 468 &FtwDevice->FtwLastWriteRecord 469 ); 470 471 // 472 // Set the WorkingBlockValid and WorkingBlockInvalid as INVALID 473 // 474 WorkingBlockHeader = (EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER *) (TempBuffer + 475 (UINTN) WorkSpaceLbaOffset * FtwDevice->WorkBlockSize + 476 FtwDevice->FtwWorkSpaceBase); 477 WorkingBlockHeader->WorkingBlockValid = FTW_INVALID_STATE; 478 WorkingBlockHeader->WorkingBlockInvalid = FTW_INVALID_STATE; 479 480 // 481 // Try to keep the content of spare block 482 // Save spare block into a spare backup memory buffer (Sparebuffer) 483 // 484 SpareBufferSize = FtwDevice->SpareAreaLength; 485 SpareBuffer = AllocatePool (SpareBufferSize); 486 if (SpareBuffer == NULL) { 487 FreePool (TempBuffer); 488 return EFI_OUT_OF_RESOURCES; 489 } 490 491 Ptr = SpareBuffer; 492 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { 493 Length = FtwDevice->SpareBlockSize; 494 Status = FtwDevice->FtwBackupFvb->Read ( 495 FtwDevice->FtwBackupFvb, 496 FtwDevice->FtwSpareLba + Index, 497 0, 498 &Length, 499 Ptr 500 ); 501 if (EFI_ERROR (Status)) { 502 FreePool (TempBuffer); 503 FreePool (SpareBuffer); 504 return EFI_ABORTED; 505 } 506 507 Ptr += Length; 508 } 509 // 510 // Write the memory buffer to spare block 511 // 512 Status = FtwEraseSpareBlock (FtwDevice); 513 if (EFI_ERROR (Status)) { 514 FreePool (TempBuffer); 515 FreePool (SpareBuffer); 516 return EFI_ABORTED; 517 } 518 Ptr = TempBuffer; 519 for (Index = 0; TempBufferSize > 0; Index += 1) { 520 if (TempBufferSize > FtwDevice->SpareBlockSize) { 521 Length = FtwDevice->SpareBlockSize; 522 } else { 523 Length = TempBufferSize; 524 } 525 Status = FtwDevice->FtwBackupFvb->Write ( 526 FtwDevice->FtwBackupFvb, 527 FtwDevice->FtwSpareLba + Index, 528 0, 529 &Length, 530 Ptr 531 ); 532 if (EFI_ERROR (Status)) { 533 FreePool (TempBuffer); 534 FreePool (SpareBuffer); 535 return EFI_ABORTED; 536 } 537 538 Ptr += Length; 539 TempBufferSize -= Length; 540 } 541 // 542 // Free TempBuffer 543 // 544 FreePool (TempBuffer); 545 546 // 547 // Set the WorkingBlockValid in spare block 548 // 549 Status = FtwUpdateFvState ( 550 FtwDevice->FtwBackupFvb, 551 FtwDevice->SpareBlockSize, 552 FtwDevice->FtwSpareLba + FtwDevice->FtwWorkSpaceLbaInSpare, 553 FtwDevice->FtwWorkSpaceBaseInSpare + sizeof (EFI_GUID) + sizeof (UINT32), 554 WORKING_BLOCK_VALID 555 ); 556 if (EFI_ERROR (Status)) { 557 FreePool (SpareBuffer); 558 return EFI_ABORTED; 559 } 560 // 561 // Before erase the working block, set WorkingBlockInvalid in working block. 562 // 563 // Offset = OFFSET_OF(EFI_FAULT_TOLERANT_WORKING_BLOCK_HEADER, 564 // WorkingBlockInvalid); 565 // 566 Status = FtwUpdateFvState ( 567 FtwDevice->FtwFvBlock, 568 FtwDevice->WorkBlockSize, 569 FtwDevice->FtwWorkSpaceLba, 570 FtwDevice->FtwWorkSpaceBase + sizeof (EFI_GUID) + sizeof (UINT32), 571 WORKING_BLOCK_INVALID 572 ); 573 if (EFI_ERROR (Status)) { 574 FreePool (SpareBuffer); 575 return EFI_ABORTED; 576 } 577 578 FtwDevice->FtwWorkSpaceHeader->WorkingBlockInvalid = FTW_VALID_STATE; 579 580 // 581 // Write the spare block to working block 582 // 583 Status = FlushSpareBlockToWorkingBlock (FtwDevice); 584 if (EFI_ERROR (Status)) { 585 FreePool (SpareBuffer); 586 return Status; 587 } 588 // 589 // Restore spare backup buffer into spare block , if no failure happened during FtwWrite. 590 // 591 Status = FtwEraseSpareBlock (FtwDevice); 592 if (EFI_ERROR (Status)) { 593 FreePool (SpareBuffer); 594 return EFI_ABORTED; 595 } 596 Ptr = SpareBuffer; 597 for (Index = 0; Index < FtwDevice->NumberOfSpareBlock; Index += 1) { 598 Length = FtwDevice->SpareBlockSize; 599 Status = FtwDevice->FtwBackupFvb->Write ( 600 FtwDevice->FtwBackupFvb, 601 FtwDevice->FtwSpareLba + Index, 602 0, 603 &Length, 604 Ptr 605 ); 606 if (EFI_ERROR (Status)) { 607 FreePool (SpareBuffer); 608 return EFI_ABORTED; 609 } 610 611 Ptr += Length; 612 } 613 614 FreePool (SpareBuffer); 615 616 DEBUG ((EFI_D_INFO, "Ftw: reclaim work space successfully\n")); 617 618 return EFI_SUCCESS; 619 } 620