]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/Drivers/NorFlashDxe/NorFlashStandaloneMm.c
ArmPlatformPkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / ArmPlatformPkg / Drivers / NorFlashDxe / NorFlashStandaloneMm.c
1 /** @file NorFlashStandaloneMm.c
2
3 Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
4 Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Library/BaseMemoryLib.h>
11 #include <Library/MemoryAllocationLib.h>
12 #include <Library/MmServicesTableLib.h>
13
14 #include "NorFlash.h"
15
16 //
17 // Global variable declarations
18 //
19 NOR_FLASH_INSTANCE **mNorFlashInstances;
20 UINT32 mNorFlashDeviceCount;
21 UINTN mFlashNvStorageVariableBase;
22
23 NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {
24 NOR_FLASH_SIGNATURE, // Signature
25 NULL, // Handle ... NEED TO BE FILLED
26
27 0, // DeviceBaseAddress ... NEED TO BE FILLED
28 0, // RegionBaseAddress ... NEED TO BE FILLED
29 0, // Size ... NEED TO BE FILLED
30 0, // StartLba
31
32 {
33 EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
34 NULL, // Media ... NEED TO BE FILLED
35 NULL, // Reset;
36 NULL, // ReadBlocks
37 NULL, // WriteBlocks
38 NULL // FlushBlocks
39 }, // BlockIoProtocol
40
41 {
42 0, // MediaId ... NEED TO BE FILLED
43 FALSE, // RemovableMedia
44 TRUE, // MediaPresent
45 FALSE, // LogicalPartition
46 FALSE, // ReadOnly
47 FALSE, // WriteCaching;
48 0, // BlockSize ... NEED TO BE FILLED
49 4, // IoAlign
50 0, // LastBlock ... NEED TO BE FILLED
51 0, // LowestAlignedLba
52 1, // LogicalBlocksPerPhysicalBlock
53 }, //Media;
54
55 {
56 EFI_DISK_IO_PROTOCOL_REVISION, // Revision
57 NULL, // ReadDisk
58 NULL // WriteDisk
59 },
60
61 {
62 FvbGetAttributes, // GetAttributes
63 FvbSetAttributes, // SetAttributes
64 FvbGetPhysicalAddress, // GetPhysicalAddress
65 FvbGetBlockSize, // GetBlockSize
66 FvbRead, // Read
67 FvbWrite, // Write
68 FvbEraseBlocks, // EraseBlocks
69 NULL, //ParentHandle
70 }, // FvbProtoccol;
71 NULL, // ShadowBuffer
72 {
73 {
74 {
75 HARDWARE_DEVICE_PATH,
76 HW_VENDOR_DP,
77 {
78 (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),
79 (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)
80 }
81 },
82 { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED
83 },
84 0, // Index
85 {
86 END_DEVICE_PATH_TYPE,
87 END_ENTIRE_DEVICE_PATH_SUBTYPE,
88 { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
89 }
90 } // DevicePath
91 };
92
93 EFI_STATUS
94 NorFlashCreateInstance (
95 IN UINTN NorFlashDeviceBase,
96 IN UINTN NorFlashRegionBase,
97 IN UINTN NorFlashSize,
98 IN UINT32 Index,
99 IN UINT32 BlockSize,
100 IN BOOLEAN SupportFvb,
101 OUT NOR_FLASH_INSTANCE** NorFlashInstance
102 )
103 {
104 EFI_STATUS Status;
105 NOR_FLASH_INSTANCE* Instance;
106
107 ASSERT(NorFlashInstance != NULL);
108
109 Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);
110 if (Instance == NULL) {
111 return EFI_OUT_OF_RESOURCES;
112 }
113
114 Instance->DeviceBaseAddress = NorFlashDeviceBase;
115 Instance->RegionBaseAddress = NorFlashRegionBase;
116 Instance->Size = NorFlashSize;
117
118 Instance->BlockIoProtocol.Media = &Instance->Media;
119 Instance->Media.MediaId = Index;
120 Instance->Media.BlockSize = BlockSize;
121 Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;
122
123 CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
124 Instance->DevicePath.Index = (UINT8)Index;
125
126 Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;
127 if (Instance->ShadowBuffer == NULL) {
128 return EFI_OUT_OF_RESOURCES;
129 }
130
131 if (SupportFvb) {
132 NorFlashFvbInitialize (Instance);
133
134 Status = gMmst->MmInstallProtocolInterface (
135 &Instance->Handle,
136 &gEfiSmmFirmwareVolumeBlockProtocolGuid,
137 EFI_NATIVE_INTERFACE,
138 &Instance->FvbProtocol
139 );
140 if (EFI_ERROR(Status)) {
141 FreePool (Instance);
142 return Status;
143 }
144 } else {
145 DEBUG((DEBUG_ERROR,"standalone MM NOR Flash driver only support FVB.\n"));
146 FreePool (Instance);
147 return EFI_UNSUPPORTED;
148 }
149
150 *NorFlashInstance = Instance;
151 return Status;
152 }
153
154 /**
155 * This function unlock and erase an entire NOR Flash block.
156 **/
157 EFI_STATUS
158 NorFlashUnlockAndEraseSingleBlock (
159 IN NOR_FLASH_INSTANCE *Instance,
160 IN UINTN BlockAddress
161 )
162 {
163 EFI_STATUS Status;
164 UINTN Index;
165
166 Index = 0;
167 // The block erase might fail a first time (SW bug ?). Retry it ...
168 do {
169 // Unlock the block if we have to
170 Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
171 if (EFI_ERROR (Status)) {
172 break;
173 }
174 Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
175 Index++;
176 } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
177
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));
180 }
181
182 return Status;
183 }
184
185 EFI_STATUS
186 NorFlashWriteFullBlock (
187 IN NOR_FLASH_INSTANCE *Instance,
188 IN EFI_LBA Lba,
189 IN UINT32 *DataBuffer,
190 IN UINT32 BlockSizeInWords
191 )
192 {
193 EFI_STATUS Status;
194 UINTN WordAddress;
195 UINT32 WordIndex;
196 UINTN BufferIndex;
197 UINTN BlockAddress;
198 UINTN BuffersInBlock;
199 UINTN RemainingWords;
200 UINTN Cnt;
201
202 Status = EFI_SUCCESS;
203
204 // Get the physical address of the block
205 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
206
207 // Start writing from the first address at the start of the block
208 WordAddress = BlockAddress;
209
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));
213 goto EXIT;
214 }
215
216 // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
217
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) {
220
221 // First, break the entire block into buffer-sized chunks.
222 BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
223
224 // Then feed each buffer chunk to the NOR Flash
225 // If a buffer does not contain any data, don't write it.
226 for(BufferIndex=0;
227 BufferIndex < BuffersInBlock;
228 BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
229 ) {
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,
235 DataBuffer);
236 if (EFI_ERROR(Status)) {
237 goto EXIT;
238 }
239 break;
240 }
241 }
242 }
243
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;
246
247 if(RemainingWords != 0) {
248 Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
249 if (EFI_ERROR(Status)) {
250 goto EXIT;
251 }
252 }
253
254 } else {
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)) {
261 goto EXIT;
262 }
263 }
264 }
265
266 EXIT:
267 if (EFI_ERROR(Status)) {
268 DEBUG((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
269 }
270 return Status;
271 }
272
273 EFI_STATUS
274 EFIAPI
275 NorFlashInitialise (
276 IN EFI_HANDLE ImageHandle,
277 IN EFI_MM_SYSTEM_TABLE *MmSystemTable
278 )
279 {
280 EFI_STATUS Status;
281 UINT32 Index;
282 NOR_FLASH_DESCRIPTION* NorFlashDevices;
283 BOOLEAN ContainVariableStorage;
284
285 Status = NorFlashPlatformInitialization ();
286 if (EFI_ERROR(Status)) {
287 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
288 return Status;
289 }
290
291 Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
292 if (EFI_ERROR(Status)) {
293 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
294 return Status;
295 }
296
297 mNorFlashInstances = AllocatePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
298
299 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
300 // Check if this NOR Flash device contain the variable storage region
301
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);
307 } else {
308 ContainVariableStorage =
309 (NorFlashDevices[Index].RegionBaseAddress <= FixedPcdGet32 (PcdFlashNvStorageVariableBase)) &&
310 (FixedPcdGet32 (PcdFlashNvStorageVariableBase) + FixedPcdGet32 (PcdFlashNvStorageVariableSize) <=
311 NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
312 }
313
314 Status = NorFlashCreateInstance (
315 NorFlashDevices[Index].DeviceBaseAddress,
316 NorFlashDevices[Index].RegionBaseAddress,
317 NorFlashDevices[Index].Size,
318 Index,
319 NorFlashDevices[Index].BlockSize,
320 ContainVariableStorage,
321 &mNorFlashInstances[Index]
322 );
323 if (EFI_ERROR(Status)) {
324 DEBUG((DEBUG_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
325 }
326 }
327
328 return Status;
329 }
330
331 EFI_STATUS
332 EFIAPI
333 NorFlashFvbInitialize (
334 IN NOR_FLASH_INSTANCE* Instance
335 )
336 {
337 EFI_STATUS Status;
338 UINT32 FvbNumLba;
339
340 ASSERT((Instance != NULL));
341
342
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;
347
348 // Determine if there is a valid header at the beginning of the NorFlash
349 Status = ValidateFvHeader (Instance);
350
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",
356 __FUNCTION__));
357
358 // Erase all the NorFlash that is reserved for variable storage
359 FvbNumLba = (PcdGet32(PcdFlashNvStorageVariableSize) + PcdGet32(PcdFlashNvStorageFtwWorkingSize) + PcdGet32(PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;
360
361 Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);
362 if (EFI_ERROR(Status)) {
363 return Status;
364 }
365
366 // Install all appropriate headers
367 Status = InitializeFvAndVariableStoreHeaders (Instance);
368 if (EFI_ERROR(Status)) {
369 return Status;
370 }
371 }
372
373 return Status;
374 }