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