1 /** @file
2   Handles non-volatile variable store garbage collection, using FTW
3   (Fault Tolerant Write) protocol.
4 
5 Copyright (c) 2006 - 2011, 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 "Variable.h"
17 
18 /**
19   Gets firmware volume block handle by given address.
20 
21   This function gets firmware volume block handle whose
22   address range contains the parameter Address.
23 
24   @param[in]  Address    Address which should be contained
25                          by returned FVB handle.
26   @param[out] FvbHandle  Pointer to FVB handle for output.
27 
28   @retval EFI_SUCCESS    FVB handle successfully returned.
29   @retval EFI_NOT_FOUND  Failed to find FVB handle by address.
30 
31 **/
32 EFI_STATUS
GetFvbHandleByAddress(IN EFI_PHYSICAL_ADDRESS Address,OUT EFI_HANDLE * FvbHandle)33 GetFvbHandleByAddress (
34   IN  EFI_PHYSICAL_ADDRESS   Address,
35   OUT EFI_HANDLE             *FvbHandle
36   )
37 {
38   EFI_STATUS                          Status;
39   EFI_HANDLE                          *HandleBuffer;
40   UINTN                               HandleCount;
41   UINTN                               Index;
42   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
43   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
44   EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;
45 
46   *FvbHandle = NULL;
47   //
48   // Locate all handles with Firmware Volume Block protocol
49   //
50   Status = gBS->LocateHandleBuffer (
51                   ByProtocol,
52                   &gEfiFirmwareVolumeBlockProtocolGuid,
53                   NULL,
54                   &HandleCount,
55                   &HandleBuffer
56                   );
57   if (EFI_ERROR (Status)) {
58     return EFI_NOT_FOUND;
59   }
60   //
61   // Traverse all the handles, searching for the one containing parameter Address
62   //
63   for (Index = 0; Index < HandleCount; Index += 1) {
64     Status = gBS->HandleProtocol (
65                     HandleBuffer[Index],
66                     &gEfiFirmwareVolumeBlockProtocolGuid,
67                     (VOID **) &Fvb
68                     );
69     if (EFI_ERROR (Status)) {
70       Status = EFI_NOT_FOUND;
71       break;
72     }
73     //
74     // Checks if the address range of this handle contains parameter Address
75     //
76     Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
77     if (EFI_ERROR (Status)) {
78       continue;
79     }
80 
81     FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
82     if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + FwVolHeader->FvLength))) {
83       *FvbHandle  = HandleBuffer[Index];
84       Status      = EFI_SUCCESS;
85       break;
86     }
87   }
88 
89   FreePool (HandleBuffer);
90   return Status;
91 }
92 
93 /**
94   Gets LBA of block and offset by given address.
95 
96   This function gets the Logical Block Address (LBA) of firmware
97   volume block containing the given address, and the offset of
98   address on the block.
99 
100   @param[in]  Address    Address which should be contained
101                          by returned FVB handle.
102   @param[out] Lba        The pointer to LBA for output.
103   @param[out] Offset     The pointer to offset for output.
104 
105   @retval EFI_SUCCESS    LBA and offset successfully returned.
106   @retval EFI_NOT_FOUND  Failed to find FVB handle by address.
107   @retval EFI_ABORTED    Failed to find valid LBA and offset.
108 
109 **/
110 EFI_STATUS
GetLbaAndOffsetByAddress(IN EFI_PHYSICAL_ADDRESS Address,OUT EFI_LBA * Lba,OUT UINTN * Offset)111 GetLbaAndOffsetByAddress (
112   IN  EFI_PHYSICAL_ADDRESS   Address,
113   OUT EFI_LBA                *Lba,
114   OUT UINTN                  *Offset
115   )
116 {
117   EFI_STATUS                          Status;
118   EFI_HANDLE                          FvbHandle;
119   EFI_PHYSICAL_ADDRESS                FvbBaseAddress;
120   EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
121   EFI_FIRMWARE_VOLUME_HEADER          *FwVolHeader;
122   EFI_FV_BLOCK_MAP_ENTRY              *FvbMapEntry;
123   UINT32                              LbaIndex;
124 
125   *Lba    = (EFI_LBA) (-1);
126   *Offset = 0;
127 
128   //
129   // Gets firmware volume block handle by given address.
130   //
131   Status = GetFvbHandleByAddress (Address, &FvbHandle);
132   if (EFI_ERROR (Status)) {
133     return Status;
134   }
135 
136   Status = gBS->HandleProtocol (
137                   FvbHandle,
138                   &gEfiFirmwareVolumeBlockProtocolGuid,
139                   (VOID **) &Fvb
140                   );
141   if (EFI_ERROR (Status)) {
142     return Status;
143   }
144   //
145   // Get the Base Address of FV
146   //
147   Status = Fvb->GetPhysicalAddress (Fvb, &FvbBaseAddress);
148   if (EFI_ERROR (Status)) {
149     return Status;
150   }
151 
152   FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINTN) FvbBaseAddress);
153 
154   //
155   // Get the (LBA, Offset) of Address
156   //
157   if ((Address >= FvbBaseAddress) && (Address <= (FvbBaseAddress + FwVolHeader->FvLength))) {
158     if ((FwVolHeader->FvLength) > (FwVolHeader->HeaderLength)) {
159       //
160       // BUGBUG: Assume one FV has one type of BlockLength
161       //
162       FvbMapEntry = &FwVolHeader->BlockMap[0];
163       for (LbaIndex = 1; LbaIndex <= FvbMapEntry->NumBlocks; LbaIndex += 1) {
164         if (Address < (FvbBaseAddress + FvbMapEntry->Length * LbaIndex)) {
165           //
166           // Found the (Lba, Offset)
167           //
168           *Lba    = LbaIndex - 1;
169           *Offset = (UINTN) (Address - (FvbBaseAddress + FvbMapEntry->Length * (LbaIndex - 1)));
170           return EFI_SUCCESS;
171         }
172       }
173     }
174   }
175 
176   return EFI_ABORTED;
177 }
178 
179 /**
180   Writes a buffer to variable storage space.
181 
182   This function writes a buffer to variable storage space into firmware
183   volume block device. The destination is specified by parameter
184   VariableBase. Fault Tolerant Write protocol is used for writing.
185 
186   @param[in] VariableBase The base address of the variable to write.
187   @param[in] Buffer       Points to the data buffer.
188   @param[in] BufferSize   The number of bytes of the data Buffer.
189 
190   @retval EFI_SUCCESS     The function completed successfully.
191   @retval EFI_NOT_FOUND   Fail to locate Fault Tolerant Write protocol.
192   @retval Other           The function could not complete successfully.
193 
194 **/
195 EFI_STATUS
FtwVariableSpace(IN EFI_PHYSICAL_ADDRESS VariableBase,IN UINT8 * Buffer,IN UINTN BufferSize)196 FtwVariableSpace (
197   IN EFI_PHYSICAL_ADDRESS   VariableBase,
198   IN UINT8                  *Buffer,
199   IN UINTN                  BufferSize
200   )
201 {
202   EFI_STATUS            Status;
203   EFI_HANDLE            FvbHandle;
204   EFI_LBA               VarLba;
205   UINTN                 VarOffset;
206   UINT8                 *FtwBuffer;
207   UINTN                 FtwBufferSize;
208   EFI_FAULT_TOLERANT_WRITE_PROTOCOL  *FtwProtocol;
209 
210   //
211   // Locate Fault Tolerant Write protocol
212   //
213   Status = gBS->LocateProtocol (
214                   &gEfiFaultTolerantWriteProtocolGuid,
215                   NULL,
216                   (VOID **) &FtwProtocol
217                   );
218   if (EFI_ERROR (Status)) {
219     return EFI_NOT_FOUND;
220   }
221   //
222   // Gets firmware volume block handle by VariableBase.
223   //
224   Status = GetFvbHandleByAddress (VariableBase, &FvbHandle);
225   if (EFI_ERROR (Status)) {
226     return Status;
227   }
228   //
229   // Gets LBA of block and offset by VariableBase.
230   //
231   Status = GetLbaAndOffsetByAddress (VariableBase, &VarLba, &VarOffset);
232   if (EFI_ERROR (Status)) {
233     return EFI_ABORTED;
234   }
235   //
236   // Prepare for the variable data
237   //
238   FtwBufferSize = ((VARIABLE_STORE_HEADER *) ((UINTN) VariableBase))->Size;
239   FtwBuffer     = AllocatePool (FtwBufferSize);
240   if (FtwBuffer == NULL) {
241     return EFI_OUT_OF_RESOURCES;
242   }
243 
244   SetMem (FtwBuffer, FtwBufferSize, (UINT8) 0xff);
245   CopyMem (FtwBuffer, Buffer, BufferSize);
246 
247   //
248   // FTW write record
249   //
250   Status = FtwProtocol->Write (
251                           FtwProtocol,
252                           VarLba,         // LBA
253                           VarOffset,      // Offset
254                           FtwBufferSize,  // NumBytes,
255                           NULL,
256                           FvbHandle,
257                           FtwBuffer
258                           );
259 
260   FreePool (FtwBuffer);
261   return Status;
262 }
263