1 /** @file NorFlashStandaloneMm.c
3 Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
4 Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
10 #include <Library/BaseMemoryLib.h>
11 #include <Library/MemoryAllocationLib.h>
12 #include <Library/MmServicesTableLib.h>
17 // Global variable declarations
19 NOR_FLASH_INSTANCE
**mNorFlashInstances
;
20 UINT32 mNorFlashDeviceCount
;
21 UINTN mFlashNvStorageVariableBase
;
23 NOR_FLASH_INSTANCE mNorFlashInstanceTemplate
= {
24 NOR_FLASH_SIGNATURE
, // Signature
25 NULL
, // Handle ... NEED TO BE FILLED
27 0, // DeviceBaseAddress ... NEED TO BE FILLED
28 0, // RegionBaseAddress ... NEED TO BE FILLED
29 0, // Size ... NEED TO BE FILLED
33 EFI_BLOCK_IO_PROTOCOL_REVISION2
, // Revision
34 NULL
, // Media ... NEED TO BE FILLED
42 0, // MediaId ... NEED TO BE FILLED
43 FALSE
, // RemovableMedia
45 FALSE
, // LogicalPartition
47 FALSE
, // WriteCaching;
48 0, // BlockSize ... NEED TO BE FILLED
50 0, // LastBlock ... NEED TO BE FILLED
51 0, // LowestAlignedLba
52 1, // LogicalBlocksPerPhysicalBlock
56 EFI_DISK_IO_PROTOCOL_REVISION
, // Revision
62 FvbGetAttributes
, // GetAttributes
63 FvbSetAttributes
, // SetAttributes
64 FvbGetPhysicalAddress
, // GetPhysicalAddress
65 FvbGetBlockSize
, // GetBlockSize
68 FvbEraseBlocks
, // EraseBlocks
78 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
)),
79 (UINT8
)(OFFSET_OF (NOR_FLASH_DEVICE_PATH
, End
) >> 8)
82 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED
87 END_ENTIRE_DEVICE_PATH_SUBTYPE
,
88 { sizeof (EFI_DEVICE_PATH_PROTOCOL
), 0 }
94 NorFlashCreateInstance (
95 IN UINTN NorFlashDeviceBase
,
96 IN UINTN NorFlashRegionBase
,
97 IN UINTN NorFlashSize
,
100 IN BOOLEAN SupportFvb
,
101 OUT NOR_FLASH_INSTANCE
** NorFlashInstance
105 NOR_FLASH_INSTANCE
* Instance
;
107 ASSERT(NorFlashInstance
!= NULL
);
109 Instance
= AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE
),&mNorFlashInstanceTemplate
);
110 if (Instance
== NULL
) {
111 return EFI_OUT_OF_RESOURCES
;
114 Instance
->DeviceBaseAddress
= NorFlashDeviceBase
;
115 Instance
->RegionBaseAddress
= NorFlashRegionBase
;
116 Instance
->Size
= NorFlashSize
;
118 Instance
->BlockIoProtocol
.Media
= &Instance
->Media
;
119 Instance
->Media
.MediaId
= Index
;
120 Instance
->Media
.BlockSize
= BlockSize
;
121 Instance
->Media
.LastBlock
= (NorFlashSize
/ BlockSize
)-1;
123 CopyGuid (&Instance
->DevicePath
.Vendor
.Guid
, &gEfiCallerIdGuid
);
124 Instance
->DevicePath
.Index
= (UINT8
)Index
;
126 Instance
->ShadowBuffer
= AllocateRuntimePool (BlockSize
);;
127 if (Instance
->ShadowBuffer
== NULL
) {
128 return EFI_OUT_OF_RESOURCES
;
132 NorFlashFvbInitialize (Instance
);
134 Status
= gMmst
->MmInstallProtocolInterface (
136 &gEfiSmmFirmwareVolumeBlockProtocolGuid
,
137 EFI_NATIVE_INTERFACE
,
138 &Instance
->FvbProtocol
140 if (EFI_ERROR(Status
)) {
145 DEBUG((DEBUG_ERROR
,"standalone MM NOR Flash driver only support FVB.\n"));
147 return EFI_UNSUPPORTED
;
150 *NorFlashInstance
= Instance
;
155 * This function unlock and erase an entire NOR Flash block.
158 NorFlashUnlockAndEraseSingleBlock (
159 IN NOR_FLASH_INSTANCE
*Instance
,
160 IN UINTN BlockAddress
167 // The block erase might fail a first time (SW bug ?). Retry it ...
169 // Unlock the block if we have to
170 Status
= NorFlashUnlockSingleBlockIfNecessary (Instance
, BlockAddress
);
171 if (EFI_ERROR (Status
)) {
174 Status
= NorFlashEraseSingleBlock (Instance
, BlockAddress
);
176 } while ((Index
< NOR_FLASH_ERASE_RETRY
) && (Status
== EFI_WRITE_PROTECTED
));
178 if (Index
== NOR_FLASH_ERASE_RETRY
) {
179 DEBUG((DEBUG_ERROR
,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress
,Index
));
186 NorFlashWriteFullBlock (
187 IN NOR_FLASH_INSTANCE
*Instance
,
189 IN UINT32
*DataBuffer
,
190 IN UINT32 BlockSizeInWords
198 UINTN BuffersInBlock
;
199 UINTN RemainingWords
;
202 Status
= EFI_SUCCESS
;
204 // Get the physical address of the block
205 BlockAddress
= GET_NOR_BLOCK_ADDRESS (Instance
->RegionBaseAddress
, Lba
, BlockSizeInWords
* 4);
207 // Start writing from the first address at the start of the block
208 WordAddress
= BlockAddress
;
210 Status
= NorFlashUnlockAndEraseSingleBlock (Instance
, BlockAddress
);
211 if (EFI_ERROR(Status
)) {
212 DEBUG((DEBUG_ERROR
, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress
));
216 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
218 // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
219 if ((WordAddress
& BOUNDARY_OF_32_WORDS
) == 0x00) {
221 // First, break the entire block into buffer-sized chunks.
222 BuffersInBlock
= (UINTN
)(BlockSizeInWords
* 4) / P30_MAX_BUFFER_SIZE_IN_BYTES
;
224 // Then feed each buffer chunk to the NOR Flash
225 // If a buffer does not contain any data, don't write it.
227 BufferIndex
< BuffersInBlock
;
228 BufferIndex
++, WordAddress
+= P30_MAX_BUFFER_SIZE_IN_BYTES
, DataBuffer
+= P30_MAX_BUFFER_SIZE_IN_WORDS
230 // Check the buffer to see if it contains any data (not set all 1s).
231 for (Cnt
= 0; Cnt
< P30_MAX_BUFFER_SIZE_IN_WORDS
; Cnt
++) {
232 if (~DataBuffer
[Cnt
] != 0 ) {
233 // Some data found, write the buffer.
234 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, P30_MAX_BUFFER_SIZE_IN_BYTES
,
236 if (EFI_ERROR(Status
)) {
244 // Finally, finish off any remaining words that are less than the maximum size of the buffer
245 RemainingWords
= BlockSizeInWords
% P30_MAX_BUFFER_SIZE_IN_WORDS
;
247 if(RemainingWords
!= 0) {
248 Status
= NorFlashWriteBuffer (Instance
, WordAddress
, (RemainingWords
* 4), DataBuffer
);
249 if (EFI_ERROR(Status
)) {
255 // For now, use the single word programming algorithm
256 // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
257 // i.e. which ends in the range 0x......01 - 0x......7F.
258 for(WordIndex
=0; WordIndex
<BlockSizeInWords
; WordIndex
++, DataBuffer
++, WordAddress
= WordAddress
+ 4) {
259 Status
= NorFlashWriteSingleWord (Instance
, WordAddress
, *DataBuffer
);
260 if (EFI_ERROR(Status
)) {
267 if (EFI_ERROR(Status
)) {
268 DEBUG((DEBUG_ERROR
, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress
, Status
));
276 IN EFI_HANDLE ImageHandle
,
277 IN EFI_MM_SYSTEM_TABLE
*MmSystemTable
282 NOR_FLASH_DESCRIPTION
* NorFlashDevices
;
283 BOOLEAN ContainVariableStorage
;
285 Status
= NorFlashPlatformInitialization ();
286 if (EFI_ERROR(Status
)) {
287 DEBUG((DEBUG_ERROR
,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
291 Status
= NorFlashPlatformGetDevices (&NorFlashDevices
, &mNorFlashDeviceCount
);
292 if (EFI_ERROR(Status
)) {
293 DEBUG((DEBUG_ERROR
,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
297 mNorFlashInstances
= AllocatePool (sizeof(NOR_FLASH_INSTANCE
*) * mNorFlashDeviceCount
);
299 for (Index
= 0; Index
< mNorFlashDeviceCount
; Index
++) {
300 // Check if this NOR Flash device contain the variable storage region
302 if (FixedPcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) {
303 ContainVariableStorage
=
304 (NorFlashDevices
[Index
].RegionBaseAddress
<= FixedPcdGet64 (PcdFlashNvStorageVariableBase64
)) &&
305 (FixedPcdGet64 (PcdFlashNvStorageVariableBase64
) + FixedPcdGet32 (PcdFlashNvStorageVariableSize
) <=
306 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
308 ContainVariableStorage
=
309 (NorFlashDevices
[Index
].RegionBaseAddress
<= FixedPcdGet32 (PcdFlashNvStorageVariableBase
)) &&
310 (FixedPcdGet32 (PcdFlashNvStorageVariableBase
) + FixedPcdGet32 (PcdFlashNvStorageVariableSize
) <=
311 NorFlashDevices
[Index
].RegionBaseAddress
+ NorFlashDevices
[Index
].Size
);
314 Status
= NorFlashCreateInstance (
315 NorFlashDevices
[Index
].DeviceBaseAddress
,
316 NorFlashDevices
[Index
].RegionBaseAddress
,
317 NorFlashDevices
[Index
].Size
,
319 NorFlashDevices
[Index
].BlockSize
,
320 ContainVariableStorage
,
321 &mNorFlashInstances
[Index
]
323 if (EFI_ERROR(Status
)) {
324 DEBUG((DEBUG_ERROR
,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index
));
333 NorFlashFvbInitialize (
334 IN NOR_FLASH_INSTANCE
* Instance
340 ASSERT((Instance
!= NULL
));
343 mFlashNvStorageVariableBase
= (FixedPcdGet64 (PcdFlashNvStorageVariableBase64
) != 0) ?
344 FixedPcdGet64 (PcdFlashNvStorageVariableBase64
) : FixedPcdGet32 (PcdFlashNvStorageVariableBase
);
345 // Set the index of the first LBA for the FVB
346 Instance
->StartLba
= (mFlashNvStorageVariableBase
- Instance
->RegionBaseAddress
) / Instance
->Media
.BlockSize
;
348 // Determine if there is a valid header at the beginning of the NorFlash
349 Status
= ValidateFvHeader (Instance
);
351 // Install the Default FVB header if required
352 if (EFI_ERROR(Status
)) {
353 // There is no valid header, so time to install one.
354 DEBUG ((DEBUG_INFO
, "%a: The FVB Header is not valid.\n", __FUNCTION__
));
355 DEBUG ((DEBUG_INFO
, "%a: Installing a correct one for this volume.\n",
358 // Erase all the NorFlash that is reserved for variable storage
359 FvbNumLba
= (PcdGet32(PcdFlashNvStorageVariableSize
) + PcdGet32(PcdFlashNvStorageFtwWorkingSize
) + PcdGet32(PcdFlashNvStorageFtwSpareSize
)) / Instance
->Media
.BlockSize
;
361 Status
= FvbEraseBlocks (&Instance
->FvbProtocol
, (EFI_LBA
)0, FvbNumLba
, EFI_LBA_LIST_TERMINATOR
);
362 if (EFI_ERROR(Status
)) {
366 // Install all appropriate headers
367 Status
= InitializeFvAndVariableStoreHeaders (Instance
);
368 if (EFI_ERROR(Status
)) {