1 /** @file
2 This file implement the Variable Protocol for the block device.
3
4 Copyright (c) 2015-2016, Linaro Limited. All rights reserved.
5 Copyright (c) 2015-2016, Hisilicon Limited. All rights reserved.
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <Guid/VariableFormat.h>
18 #include <Guid/SystemNvDataGuid.h>
19
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/CacheMaintenanceLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/DevicePathLib.h>
24 #include <Library/IoLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/PcdLib.h>
27 #include <Library/UefiBootServicesTableLib.h>
28 #include <Library/UefiLib.h>
29
30 #include "BlockVariableDxe.h"
31
32
33 STATIC EFI_PHYSICAL_ADDRESS mMapNvStorageVariableBase;
34
35 EFI_STATUS
36 EFIAPI
FvbGetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,OUT EFI_FVB_ATTRIBUTES_2 * Attributes)37 FvbGetAttributes (
38 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
39 OUT EFI_FVB_ATTRIBUTES_2 *Attributes
40 )
41 {
42 EFI_FVB_ATTRIBUTES_2 FvbAttributes;
43 FvbAttributes = (EFI_FVB_ATTRIBUTES_2) (
44
45 EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled
46 EFI_FVB2_READ_STATUS | // Reads are currently enabled
47 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
48 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped
49 EFI_FVB2_ERASE_POLARITY // After erasure all bits take this value (i.e. '1')
50
51 );
52 FvbAttributes |= EFI_FVB2_WRITE_STATUS | // Writes are currently enabled
53 EFI_FVB2_WRITE_ENABLED_CAP; // Writes may be enabled
54
55 *Attributes = FvbAttributes;
56
57 DEBUG ((DEBUG_BLKIO, "FvbGetAttributes(0x%X)\n", *Attributes));
58 return EFI_SUCCESS;
59 }
60
61 EFI_STATUS
62 EFIAPI
FvbSetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN OUT EFI_FVB_ATTRIBUTES_2 * Attributes)63 FvbSetAttributes(
64 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
65 IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
66 )
67 {
68 DEBUG ((DEBUG_BLKIO, "FvbSetAttributes(0x%X) is not supported\n",*Attributes));
69 return EFI_UNSUPPORTED;
70 }
71
72 EFI_STATUS
73 EFIAPI
FvbGetPhysicalAddress(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,OUT EFI_PHYSICAL_ADDRESS * Address)74 FvbGetPhysicalAddress (
75 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
76 OUT EFI_PHYSICAL_ADDRESS *Address
77 )
78 {
79 *Address = mMapNvStorageVariableBase;
80 return EFI_SUCCESS;
81 }
82
83 EFI_STATUS
84 EFIAPI
FvbGetBlockSize(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,OUT UINTN * BlockSize,OUT UINTN * NumberOfBlocks)85 FvbGetBlockSize (
86 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
87 IN EFI_LBA Lba,
88 OUT UINTN *BlockSize,
89 OUT UINTN *NumberOfBlocks
90 )
91 {
92 BLOCK_VARIABLE_INSTANCE *Instance;
93
94 Instance = CR (This, BLOCK_VARIABLE_INSTANCE, FvbProtocol, BLOCK_VARIABLE_SIGNATURE);
95 *BlockSize = (UINTN) Instance->Media.BlockSize;
96 *NumberOfBlocks = PcdGet32 (PcdNvStorageVariableBlockCount);
97 return EFI_SUCCESS;
98 }
99
100 EFI_STATUS
101 EFIAPI
FvbRead(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN OUT UINT8 * Buffer)102 FvbRead (
103 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
104 IN EFI_LBA Lba,
105 IN UINTN Offset,
106 IN OUT UINTN *NumBytes,
107 IN OUT UINT8 *Buffer
108 )
109 {
110 BLOCK_VARIABLE_INSTANCE *Instance;
111 EFI_BLOCK_IO_PROTOCOL *BlockIo;
112 EFI_STATUS Status;
113 UINTN Bytes;
114 VOID *DataPtr;
115
116 Instance = CR (This, BLOCK_VARIABLE_INSTANCE, FvbProtocol, BLOCK_VARIABLE_SIGNATURE);
117 BlockIo = Instance->BlockIoProtocol;
118 Bytes = (Offset + *NumBytes + Instance->Media.BlockSize - 1) / Instance->Media.BlockSize * Instance->Media.BlockSize;
119 DataPtr = AllocateZeroPool (Bytes);
120 if (DataPtr == NULL) {
121 DEBUG ((EFI_D_ERROR, "FvbWrite: failed to allocate buffer.\n"));
122 return EFI_BUFFER_TOO_SMALL;
123 }
124 WriteBackDataCacheRange (DataPtr, Bytes);
125 InvalidateDataCacheRange (Buffer, *NumBytes);
126 Status = BlockIo->ReadBlocks (BlockIo, BlockIo->Media->MediaId, Instance->StartLba + Lba,
127 Bytes, DataPtr);
128 if (EFI_ERROR (Status)) {
129 DEBUG ((EFI_D_ERROR, "FvbRead StartLba:%x, Lba:%x, Offset:%x, Status:%x\n",
130 Instance->StartLba, Lba, Offset, Status));
131 goto exit;
132 }
133 CopyMem (Buffer, DataPtr + Offset, *NumBytes);
134 WriteBackDataCacheRange (Buffer, *NumBytes);
135 exit:
136 FreePool (DataPtr);
137 return Status;
138 }
139
140 EFI_STATUS
141 EFIAPI
FvbWrite(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)142 FvbWrite (
143 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
144 IN EFI_LBA Lba,
145 IN UINTN Offset,
146 IN OUT UINTN *NumBytes,
147 IN UINT8 *Buffer
148 )
149 {
150 BLOCK_VARIABLE_INSTANCE *Instance;
151 EFI_BLOCK_IO_PROTOCOL *BlockIo;
152 EFI_STATUS Status;
153 UINTN Bytes;
154 VOID *DataPtr;
155
156 Instance = CR (This, BLOCK_VARIABLE_INSTANCE, FvbProtocol, BLOCK_VARIABLE_SIGNATURE);
157 BlockIo = Instance->BlockIoProtocol;
158 Bytes = (Offset + *NumBytes + Instance->Media.BlockSize - 1) / Instance->Media.BlockSize * Instance->Media.BlockSize;
159 DataPtr = AllocateZeroPool (Bytes);
160 if (DataPtr == NULL) {
161 DEBUG ((EFI_D_ERROR, "FvbWrite: failed to allocate buffer.\n"));
162 return EFI_BUFFER_TOO_SMALL;
163 }
164 WriteBackDataCacheRange (DataPtr, Bytes);
165 Status = BlockIo->ReadBlocks (BlockIo, BlockIo->Media->MediaId, Instance->StartLba + Lba,
166 Bytes, DataPtr);
167 if (EFI_ERROR (Status)) {
168 DEBUG ((EFI_D_ERROR, "FvbWrite: failed on reading blocks.\n"));
169 goto exit;
170 }
171 CopyMem (DataPtr + Offset, Buffer, *NumBytes);
172 WriteBackDataCacheRange (DataPtr, Bytes);
173 Status = BlockIo->WriteBlocks (BlockIo, BlockIo->Media->MediaId, Instance->StartLba + Lba,
174 Bytes, DataPtr);
175 if (EFI_ERROR (Status)) {
176 DEBUG ((EFI_D_ERROR, "FvbWrite StartLba:%x, Lba:%x, Offset:%x, Status:%x\n",
177 Instance->StartLba, Lba, Offset, Status));
178 }
179 // Sometimes the variable isn't flushed into block device if it's the last flush operation.
180 // So flush it again.
181 Status = BlockIo->WriteBlocks (BlockIo, BlockIo->Media->MediaId, Instance->StartLba + Lba,
182 Bytes, DataPtr);
183 if (EFI_ERROR (Status)) {
184 DEBUG ((EFI_D_ERROR, "FvbWrite StartLba:%x, Lba:%x, Offset:%x, Status:%x\n",
185 Instance->StartLba, Lba, Offset, Status));
186 }
187 exit:
188 FreePool (DataPtr);
189 return Status;
190 }
191
192 EFI_STATUS
193 EFIAPI
FvbEraseBlocks(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,...)194 FvbEraseBlocks (
195 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
196 ...
197 )
198 {
199 return EFI_SUCCESS;
200 }
201
202 STATIC BLOCK_VARIABLE_INSTANCE mBlockVariableInstance = {
203 .Signature = BLOCK_VARIABLE_SIGNATURE,
204 .Media = {
205 .MediaId = 0,
206 .RemovableMedia = FALSE,
207 .MediaPresent = TRUE,
208 .LogicalPartition = TRUE,
209 .ReadOnly = FALSE,
210 .WriteCaching = FALSE,
211 .BlockSize = 0,
212 .IoAlign = 4,
213 .LastBlock = 0,
214 .LowestAlignedLba = 0,
215 .LogicalBlocksPerPhysicalBlock = 0,
216 },
217 .FvbProtocol = {
218 .GetAttributes = FvbGetAttributes,
219 .SetAttributes = FvbSetAttributes,
220 .GetPhysicalAddress = FvbGetPhysicalAddress,
221 .GetBlockSize = FvbGetBlockSize,
222 .Read = FvbRead,
223 .Write = FvbWrite,
224 .EraseBlocks = FvbEraseBlocks,
225 }
226 };
227
228 EFI_STATUS
ValidateFvHeader(IN EFI_FIRMWARE_VOLUME_HEADER * FwVolHeader)229 ValidateFvHeader (
230 IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader
231 )
232 {
233 UINT16 Checksum, TempChecksum;
234 VARIABLE_STORE_HEADER *VariableStoreHeader;
235 UINTN VariableStoreLength;
236 UINTN FvLength;
237
238 FvLength = (UINTN) (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) +
239 PcdGet32(PcdFlashNvStorageFtwSpareSize));
240
241 //
242 // Verify the header revision, header signature, length
243 // Length of FvBlock cannot be 2**64-1
244 // HeaderLength cannot be an odd number
245 //
246 if ( (FwVolHeader->Revision != EFI_FVH_REVISION)
247 || (FwVolHeader->Signature != EFI_FVH_SIGNATURE)
248 || (FwVolHeader->FvLength != FvLength)
249 )
250 {
251 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: No Firmware Volume header present\n"));
252 return EFI_NOT_FOUND;
253 }
254
255 // Check the Firmware Volume Guid
256 if( CompareGuid (&FwVolHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid) == FALSE ) {
257 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Firmware Volume Guid non-compatible\n"));
258 return EFI_NOT_FOUND;
259 }
260
261 // Verify the header checksum
262 TempChecksum = FwVolHeader->Checksum;
263 FwVolHeader->Checksum = 0;
264 Checksum = CalculateSum16((UINT16*)FwVolHeader, FwVolHeader->HeaderLength);
265 if (Checksum != TempChecksum) {
266 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: FV checksum is invalid (Checksum:0x%X)\n",Checksum));
267 return EFI_NOT_FOUND;
268 }
269 FwVolHeader->Checksum = Checksum;
270
271 VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)FwVolHeader + FwVolHeader->HeaderLength);
272
273 // Check the Variable Store Guid
274 if( CompareGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid) == FALSE ) {
275 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Variable Store Guid non-compatible\n"));
276 return EFI_NOT_FOUND;
277 }
278
279 VariableStoreLength = PcdGet32 (PcdFlashNvStorageVariableSize) - FwVolHeader->HeaderLength;
280 if (VariableStoreHeader->Size != VariableStoreLength) {
281 DEBUG ((EFI_D_ERROR, "ValidateFvHeader: Variable Store Length does not match\n"));
282 return EFI_NOT_FOUND;
283 }
284
285 return EFI_SUCCESS;
286 }
287
288 EFI_STATUS
InitNonVolatileVariableStore(IN BLOCK_VARIABLE_INSTANCE * Instance,IN VOID * Headers,IN UINTN HeadersLength)289 InitNonVolatileVariableStore (
290 IN BLOCK_VARIABLE_INSTANCE *Instance,
291 IN VOID *Headers,
292 IN UINTN HeadersLength
293 )
294 {
295 EFI_FIRMWARE_VOLUME_HEADER *FirmwareVolumeHeader;
296 EFI_STATUS Status;
297 VARIABLE_STORE_HEADER *VariableStoreHeader;
298
299 // Check if the size of the area is at least one block size
300 ASSERT((PcdGet32(PcdFlashNvStorageVariableSize) > 0) && (PcdGet32(PcdFlashNvStorageVariableSize) / Instance->BlockIoProtocol->Media->BlockSize > 0));
301 ASSERT((PcdGet32(PcdFlashNvStorageFtwWorkingSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwWorkingSize) / Instance->BlockIoProtocol->Media->BlockSize > 0));
302 ASSERT((PcdGet32(PcdFlashNvStorageFtwSpareSize) > 0) && (PcdGet32(PcdFlashNvStorageFtwSpareSize) / Instance->BlockIoProtocol->Media->BlockSize > 0));
303
304 //
305 // EFI_FIRMWARE_VOLUME_HEADER
306 //
307 FirmwareVolumeHeader = (EFI_FIRMWARE_VOLUME_HEADER *)Headers;
308 CopyGuid (&FirmwareVolumeHeader->FileSystemGuid, &gEfiSystemNvDataFvGuid);
309 FirmwareVolumeHeader->FvLength =
310 PcdGet32(PcdFlashNvStorageVariableSize) +
311 PcdGet32(PcdFlashNvStorageFtwWorkingSize) +
312 PcdGet32(PcdFlashNvStorageFtwSpareSize);
313 FirmwareVolumeHeader->Signature = EFI_FVH_SIGNATURE;
314 FirmwareVolumeHeader->Attributes = (EFI_FVB_ATTRIBUTES_2) (
315 EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled
316 EFI_FVB2_READ_STATUS | // Reads are currently enabled
317 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
318 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped
319 EFI_FVB2_ERASE_POLARITY | // After erasure all bits take this value (i.e. '1')
320 EFI_FVB2_WRITE_STATUS | // Writes are currently enabled
321 EFI_FVB2_WRITE_ENABLED_CAP // Writes may be enabled
322 );
323 FirmwareVolumeHeader->HeaderLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY);
324 FirmwareVolumeHeader->Revision = EFI_FVH_REVISION;
325 FirmwareVolumeHeader->BlockMap[0].NumBlocks = PcdGet32 (PcdNvStorageVariableBlockCount);
326 FirmwareVolumeHeader->BlockMap[0].Length = Instance->BlockIoProtocol->Media->BlockSize;
327 // BlockMap Terminator
328 FirmwareVolumeHeader->BlockMap[1].NumBlocks = 0;
329 FirmwareVolumeHeader->BlockMap[1].Length = 0;
330 FirmwareVolumeHeader->Checksum = 0;
331 FirmwareVolumeHeader->Checksum = CalculateSum16 ((UINT16*)FirmwareVolumeHeader, FirmwareVolumeHeader->HeaderLength);
332
333 //
334 // VARIABLE_STORE_HEADER
335 //
336 VariableStoreHeader = (VARIABLE_STORE_HEADER*)((UINTN)FirmwareVolumeHeader + FirmwareVolumeHeader->HeaderLength);
337 CopyGuid (&VariableStoreHeader->Signature, &gEfiVariableGuid);
338 VariableStoreHeader->Size = PcdGet32(PcdFlashNvStorageVariableSize) - FirmwareVolumeHeader->HeaderLength;
339 VariableStoreHeader->Format = VARIABLE_STORE_FORMATTED;
340 VariableStoreHeader->State = VARIABLE_STORE_HEALTHY;
341
342 Status = FvbWrite (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers);
343 return Status;
344 }
345
346 EFI_STATUS
BlockVariableDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)347 BlockVariableDxeInitialize (
348 IN EFI_HANDLE ImageHandle,
349 IN EFI_SYSTEM_TABLE *SystemTable
350 )
351 {
352 EFI_HANDLE Handle;
353 EFI_STATUS Status;
354 BLOCK_VARIABLE_INSTANCE *Instance = &mBlockVariableInstance;
355 UINT32 Count;
356 EFI_LBA Lba;
357 UINTN NvStorageSize;
358 EFI_DEVICE_PATH_PROTOCOL *NvBlockDevicePath;
359 UINT8 *NvStorageData;
360 VOID *Headers;
361 UINTN HeadersLength;
362
363 Instance->Signature = BLOCK_VARIABLE_SIGNATURE;
364
365 HeadersLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(VARIABLE_STORE_HEADER);
366 Headers = AllocateZeroPool(HeadersLength);
367 if (Headers == NULL) {
368 DEBUG ((EFI_D_ERROR, "%a: failed to allocate memory of Headers\n", __func__));
369 return EFI_OUT_OF_RESOURCES;
370 }
371
372 Lba = (EFI_LBA) PcdGet32 (PcdNvStorageVariableBlockLba);
373 Count = PcdGet32 (PcdNvStorageVariableBlockCount);
374 Instance->Media.BlockSize = PcdGet32 (PcdNvStorageVariableBlockSize);
375 NvStorageSize = Count * Instance->Media.BlockSize;
376 Instance->StartLba = Lba;
377 HeadersLength = sizeof(EFI_FIRMWARE_VOLUME_HEADER) + sizeof(EFI_FV_BLOCK_MAP_ENTRY) + sizeof(VARIABLE_STORE_HEADER);
378 if (NvStorageSize < HeadersLength) {
379 return EFI_BAD_BUFFER_SIZE;
380 }
381 NvStorageData = (UINT8 *) (UINTN) PcdGet32(PcdFlashNvStorageVariableBase);
382 mMapNvStorageVariableBase = PcdGet32(PcdFlashNvStorageVariableBase);
383 NvBlockDevicePath = &Instance->DevicePath;
384 NvBlockDevicePath = ConvertTextToDevicePath ((CHAR16*)FixedPcdGetPtr (PcdNvStorageVariableBlockDevicePath));
385 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &NvBlockDevicePath,
386 &Instance->Handle);
387 if (EFI_ERROR (Status)) {
388 DEBUG ((EFI_D_ERROR, "Warning: Couldn't locate NVM device (status: %r)\n", Status));
389 return EFI_INVALID_PARAMETER;
390 }
391 Status = gBS->OpenProtocol (
392 Instance->Handle,
393 &gEfiBlockIoProtocolGuid,
394 (VOID **) &Instance->BlockIoProtocol,
395 gImageHandle,
396 NULL,
397 EFI_OPEN_PROTOCOL_GET_PROTOCOL
398 );
399 if (EFI_ERROR (Status)) {
400 DEBUG ((EFI_D_ERROR, "Warning: Couldn't open NVM device (status: %r)\n", Status));
401 return EFI_DEVICE_ERROR;
402 }
403 WriteBackDataCacheRange (Instance, sizeof(BLOCK_VARIABLE_INSTANCE));
404
405 Handle = NULL;
406 Status = gBS->InstallMultipleProtocolInterfaces (
407 &Handle,
408 &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol,
409 NULL
410 );
411 if (EFI_ERROR (Status)) {
412 goto exit;
413 }
414
415 Status = FvbRead (&Instance->FvbProtocol, 0, 0, &HeadersLength, Headers);
416 if (EFI_ERROR (Status)) {
417 return Status;
418 }
419
420 Status = ValidateFvHeader (Headers);
421 if (EFI_ERROR (Status)) {
422 DEBUG ((EFI_D_ERROR, "%a, Found invalid Fv Header\n", __func__));
423
424 // Erase all the block device that is reserved for variable storage
425 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, Count, EFI_LBA_LIST_TERMINATOR);
426 if (EFI_ERROR (Status)) {
427 goto exit;
428 }
429
430 Status = InitNonVolatileVariableStore (Instance, Headers, HeadersLength);
431 if (EFI_ERROR (Status)) {
432 goto exit;
433 }
434 }
435
436 if (NvStorageSize > ((EFI_FIRMWARE_VOLUME_HEADER*)Headers)->FvLength) {
437 NvStorageSize = ((EFI_FIRMWARE_VOLUME_HEADER*)Headers)->FvLength;
438 NvStorageSize = ((NvStorageSize + Instance->Media.BlockSize - 1) / Instance->Media.BlockSize) * Instance->Media.BlockSize;
439 }
440 Status = FvbRead (&Instance->FvbProtocol, 0, 0, &NvStorageSize, NvStorageData);
441
442 exit:
443 return Status;
444 }
445