1 /** @file
2   Handles non-volatile variable store garbage collection, using FTW
3   (Fault Tolerant Write) protocol.
4 
5 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "Variable.h"
11 
12 /**
13   Gets LBA of block and offset by given address.
14 
15   This function gets the Logical Block Address (LBA) of a firmware
16   volume block containing the given address, and the offset of the
17   address on the block.
18 
19   @param  Address        Address which should be contained
20                          by returned FVB handle.
21   @param  Lba            Pointer to LBA for output.
22   @param  Offset         Pointer to offset for output.
23 
24   @retval EFI_SUCCESS    LBA and offset successfully returned.
25   @retval EFI_NOT_FOUND  Fail to find FVB handle by address.
26   @retval EFI_ABORTED    Fail to find valid LBA and offset.
27 
28 **/
29 EFI_STATUS
GetLbaAndOffsetByAddress(IN EFI_PHYSICAL_ADDRESS Address,OUT EFI_LBA * Lba,OUT UINTN * Offset)30 GetLbaAndOffsetByAddress (
31   IN  EFI_PHYSICAL_ADDRESS   Address,
32   OUT EFI_LBA                *Lba,
33   OUT UINTN                  *Offset
34   )
35 {
36   EFI_STATUS                          Status;
37   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
38   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
39   EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;
40   EFI_FV_BLOCK_MAP_ENTRY              *FvbMapEntry;
41   UINT32                              LbaIndex;
42 
43   Fvb     = NULL;
44   *Lba    = (EFI_LBA) (-1);
45   *Offset = 0;
46 
47   //
48   // Get the proper FVB protocol.
49   //
50   Status = GetFvbInfoByAddress (Address, NULL, &Fvb);
51   if (EFI_ERROR (Status)) {
52     return Status;
53   }
54 
55   //
56   // Get the Base Address of FV.
57   //
58   Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
59   if (EFI_ERROR (Status)) {
60     return Status;
61   }
62 
63   FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
64 
65   //
66   // Get the (LBA, Offset) of Address.
67   //
68   if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
69     //
70     // BUGBUG: Assume one FV has one type of BlockLength.
71     //
72     FvbMapEntry = &FwVolHeader->BlockMap[0];
73     for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
74       if (Address < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) {
75         //
76         // Found the (Lba, Offset).
77         //
78         *Lba    = LbaIndex - 1;
79         *Offset = (UINTN) (Address - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));
80         return EFI_SUCCESS;
81      }
82     }
83   }
84 
85   return EFI_ABORTED;
86 }
87 
88 /**
89   Writes a buffer to variable storage space, in the working block.
90 
91   This function writes a buffer to variable storage space into a firmware
92   volume block device. The destination is specified by parameter
93   VariableBase. Fault Tolerant Write protocol is used for writing.
94 
95   @param  VariableBase   Base address of variable to write
96   @param  VariableBuffer Point to the variable data buffer.
97 
98   @retval EFI_SUCCESS    The function completed successfully.
99   @retval EFI_NOT_FOUND  Fail to locate Fault Tolerant Write protocol.
100   @retval EFI_ABORTED    The function could not complete successfully.
101 
102 **/
103 EFI_STATUS
FtwVariableSpace(IN EFI_PHYSICAL_ADDRESS VariableBase,IN VARIABLE_STORE_HEADER * VariableBuffer)104 FtwVariableSpace (
105   IN EFI_PHYSICAL_ADDRESS   VariableBase,
106   IN VARIABLE_STORE_HEADER  *VariableBuffer
107   )
108 {
109   EFI_STATUS                         Status;
110   EFI_HANDLE                         FvbHandle;
111   EFI_LBA                            VarLba;
112   UINTN                              VarOffset;
113   UINTN                              FtwBufferSize;
114   EFI_FAULT_TOLERANT_WRITE_PROTOCOL  *FtwProtocol;
115 
116   //
117   // Locate fault tolerant write protocol.
118   //
119   Status = GetFtwProtocol((VOID **) &FtwProtocol);
120   if (EFI_ERROR (Status)) {
121     return EFI_NOT_FOUND;
122   }
123   //
124   // Locate Fvb handle by address.
125   //
126   Status = GetFvbInfoByAddress (VariableBase, &FvbHandle, NULL);
127   if (EFI_ERROR (Status)) {
128     return Status;
129   }
130   //
131   // Get LBA and Offset by address.
132   //
133   Status = GetLbaAndOffsetByAddress (VariableBase, &VarLba, &VarOffset);
134   if (EFI_ERROR (Status)) {
135     return EFI_ABORTED;
136   }
137 
138   FtwBufferSize = ((VARIABLE_STORE_HEADER *) ((UINTN) VariableBase))->Size;
139   ASSERT (FtwBufferSize == VariableBuffer->Size);
140 
141   //
142   // FTW write record.
143   //
144   Status = FtwProtocol->Write (
145                           FtwProtocol,
146                           VarLba,         // LBA
147                           VarOffset,      // Offset
148                           FtwBufferSize,  // NumBytes
149                           NULL,           // PrivateData NULL
150                           FvbHandle,      // Fvb Handle
151                           (VOID *) VariableBuffer // write buffer
152                           );
153 
154   return Status;
155 }
156