NorFlashDxe.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /** @file NorFlashDxe.c
  2. Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.<BR>
  3. SPDX-License-Identifier: BSD-2-Clause-Patent
  4. **/
  5. #include <Library/UefiLib.h>
  6. #include <Library/BaseMemoryLib.h>
  7. #include <Library/MemoryAllocationLib.h>
  8. #include <Library/UefiBootServicesTableLib.h>
  9. #include <Library/PcdLib.h>
  10. #include <Library/HobLib.h>
  11. #include <Library/DxeServicesTableLib.h>
  12. #include "NorFlash.h"
  13. STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;
  14. //
  15. // Global variable declarations
  16. //
  17. NOR_FLASH_INSTANCE **mNorFlashInstances;
  18. UINT32 mNorFlashDeviceCount;
  19. UINTN mFlashNvStorageVariableBase;
  20. EFI_EVENT mFvbVirtualAddrChangeEvent;
  21. NOR_FLASH_INSTANCE mNorFlashInstanceTemplate = {
  22. NOR_FLASH_SIGNATURE, // Signature
  23. NULL, // Handle ... NEED TO BE FILLED
  24. 0, // DeviceBaseAddress ... NEED TO BE FILLED
  25. 0, // RegionBaseAddress ... NEED TO BE FILLED
  26. 0, // Size ... NEED TO BE FILLED
  27. 0, // StartLba
  28. {
  29. EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
  30. NULL, // Media ... NEED TO BE FILLED
  31. NorFlashBlockIoReset, // Reset;
  32. NorFlashBlockIoReadBlocks, // ReadBlocks
  33. NorFlashBlockIoWriteBlocks, // WriteBlocks
  34. NorFlashBlockIoFlushBlocks // FlushBlocks
  35. }, // BlockIoProtocol
  36. {
  37. 0, // MediaId ... NEED TO BE FILLED
  38. FALSE, // RemovableMedia
  39. TRUE, // MediaPresent
  40. FALSE, // LogicalPartition
  41. FALSE, // ReadOnly
  42. FALSE, // WriteCaching;
  43. 0, // BlockSize ... NEED TO BE FILLED
  44. 4, // IoAlign
  45. 0, // LastBlock ... NEED TO BE FILLED
  46. 0, // LowestAlignedLba
  47. 1, // LogicalBlocksPerPhysicalBlock
  48. }, // Media;
  49. {
  50. EFI_DISK_IO_PROTOCOL_REVISION, // Revision
  51. NorFlashDiskIoReadDisk, // ReadDisk
  52. NorFlashDiskIoWriteDisk // WriteDisk
  53. },
  54. {
  55. FvbGetAttributes, // GetAttributes
  56. FvbSetAttributes, // SetAttributes
  57. FvbGetPhysicalAddress, // GetPhysicalAddress
  58. FvbGetBlockSize, // GetBlockSize
  59. FvbRead, // Read
  60. FvbWrite, // Write
  61. FvbEraseBlocks, // EraseBlocks
  62. NULL, // ParentHandle
  63. }, // FvbProtoccol;
  64. NULL, // ShadowBuffer
  65. {
  66. {
  67. {
  68. HARDWARE_DEVICE_PATH,
  69. HW_VENDOR_DP,
  70. {
  71. (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End)),
  72. (UINT8)(OFFSET_OF (NOR_FLASH_DEVICE_PATH, End) >> 8)
  73. }
  74. },
  75. { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 }
  76. }, // GUID ... NEED TO BE FILLED
  77. },
  78. 0, // Index
  79. {
  80. END_DEVICE_PATH_TYPE,
  81. END_ENTIRE_DEVICE_PATH_SUBTYPE,
  82. { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
  83. }
  84. } // DevicePath
  85. };
  86. EFI_STATUS
  87. NorFlashCreateInstance (
  88. IN UINTN NorFlashDeviceBase,
  89. IN UINTN NorFlashRegionBase,
  90. IN UINTN NorFlashSize,
  91. IN UINT32 Index,
  92. IN UINT32 BlockSize,
  93. IN BOOLEAN SupportFvb,
  94. OUT NOR_FLASH_INSTANCE **NorFlashInstance
  95. )
  96. {
  97. EFI_STATUS Status;
  98. NOR_FLASH_INSTANCE *Instance;
  99. ASSERT (NorFlashInstance != NULL);
  100. Instance = AllocateRuntimeCopyPool (sizeof (NOR_FLASH_INSTANCE), &mNorFlashInstanceTemplate);
  101. if (Instance == NULL) {
  102. return EFI_OUT_OF_RESOURCES;
  103. }
  104. Instance->DeviceBaseAddress = NorFlashDeviceBase;
  105. Instance->RegionBaseAddress = NorFlashRegionBase;
  106. Instance->Size = NorFlashSize;
  107. Instance->BlockIoProtocol.Media = &Instance->Media;
  108. Instance->Media.MediaId = Index;
  109. Instance->Media.BlockSize = BlockSize;
  110. Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;
  111. CopyGuid (&Instance->DevicePath.Vendor.Guid, &gEfiCallerIdGuid);
  112. Instance->DevicePath.Index = (UINT8)Index;
  113. Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);
  114. if (Instance->ShadowBuffer == NULL) {
  115. return EFI_OUT_OF_RESOURCES;
  116. }
  117. if (SupportFvb) {
  118. NorFlashFvbInitialize (Instance);
  119. Status = gBS->InstallMultipleProtocolInterfaces (
  120. &Instance->Handle,
  121. &gEfiDevicePathProtocolGuid,
  122. &Instance->DevicePath,
  123. &gEfiBlockIoProtocolGuid,
  124. &Instance->BlockIoProtocol,
  125. &gEfiFirmwareVolumeBlockProtocolGuid,
  126. &Instance->FvbProtocol,
  127. NULL
  128. );
  129. if (EFI_ERROR (Status)) {
  130. FreePool (Instance);
  131. return Status;
  132. }
  133. } else {
  134. Status = gBS->InstallMultipleProtocolInterfaces (
  135. &Instance->Handle,
  136. &gEfiDevicePathProtocolGuid,
  137. &Instance->DevicePath,
  138. &gEfiBlockIoProtocolGuid,
  139. &Instance->BlockIoProtocol,
  140. &gEfiDiskIoProtocolGuid,
  141. &Instance->DiskIoProtocol,
  142. NULL
  143. );
  144. if (EFI_ERROR (Status)) {
  145. FreePool (Instance);
  146. return Status;
  147. }
  148. }
  149. *NorFlashInstance = Instance;
  150. return Status;
  151. }
  152. /**
  153. * This function unlock and erase an entire NOR Flash block.
  154. **/
  155. EFI_STATUS
  156. NorFlashUnlockAndEraseSingleBlock (
  157. IN NOR_FLASH_INSTANCE *Instance,
  158. IN UINTN BlockAddress
  159. )
  160. {
  161. EFI_STATUS Status;
  162. UINTN Index;
  163. EFI_TPL OriginalTPL;
  164. if (!EfiAtRuntime ()) {
  165. // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
  166. OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
  167. } else {
  168. // This initialization is only to prevent the compiler to complain about the
  169. // use of uninitialized variables
  170. OriginalTPL = TPL_HIGH_LEVEL;
  171. }
  172. Index = 0;
  173. // The block erase might fail a first time (SW bug ?). Retry it ...
  174. do {
  175. // Unlock the block if we have to
  176. Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
  177. if (EFI_ERROR (Status)) {
  178. break;
  179. }
  180. Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
  181. Index++;
  182. } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
  183. if (Index == NOR_FLASH_ERASE_RETRY) {
  184. DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress, Index));
  185. }
  186. if (!EfiAtRuntime ()) {
  187. // Interruptions can resume.
  188. gBS->RestoreTPL (OriginalTPL);
  189. }
  190. return Status;
  191. }
  192. EFI_STATUS
  193. NorFlashWriteFullBlock (
  194. IN NOR_FLASH_INSTANCE *Instance,
  195. IN EFI_LBA Lba,
  196. IN UINT32 *DataBuffer,
  197. IN UINT32 BlockSizeInWords
  198. )
  199. {
  200. EFI_STATUS Status;
  201. UINTN WordAddress;
  202. UINT32 WordIndex;
  203. UINTN BufferIndex;
  204. UINTN BlockAddress;
  205. UINTN BuffersInBlock;
  206. UINTN RemainingWords;
  207. EFI_TPL OriginalTPL;
  208. UINTN Cnt;
  209. Status = EFI_SUCCESS;
  210. // Get the physical address of the block
  211. BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
  212. // Start writing from the first address at the start of the block
  213. WordAddress = BlockAddress;
  214. if (!EfiAtRuntime ()) {
  215. // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
  216. OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
  217. } else {
  218. // This initialization is only to prevent the compiler to complain about the
  219. // use of uninitialized variables
  220. OriginalTPL = TPL_HIGH_LEVEL;
  221. }
  222. Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
  223. if (EFI_ERROR (Status)) {
  224. DEBUG ((DEBUG_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
  225. goto EXIT;
  226. }
  227. // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
  228. // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
  229. if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
  230. // First, break the entire block into buffer-sized chunks.
  231. BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
  232. // Then feed each buffer chunk to the NOR Flash
  233. // If a buffer does not contain any data, don't write it.
  234. for (BufferIndex = 0;
  235. BufferIndex < BuffersInBlock;
  236. BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
  237. )
  238. {
  239. // Check the buffer to see if it contains any data (not set all 1s).
  240. for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
  241. if (~DataBuffer[Cnt] != 0 ) {
  242. // Some data found, write the buffer.
  243. Status = NorFlashWriteBuffer (
  244. Instance,
  245. WordAddress,
  246. P30_MAX_BUFFER_SIZE_IN_BYTES,
  247. DataBuffer
  248. );
  249. if (EFI_ERROR (Status)) {
  250. goto EXIT;
  251. }
  252. break;
  253. }
  254. }
  255. }
  256. // Finally, finish off any remaining words that are less than the maximum size of the buffer
  257. RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
  258. if (RemainingWords != 0) {
  259. Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
  260. if (EFI_ERROR (Status)) {
  261. goto EXIT;
  262. }
  263. }
  264. } else {
  265. // For now, use the single word programming algorithm
  266. // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
  267. // i.e. which ends in the range 0x......01 - 0x......7F.
  268. for (WordIndex = 0; WordIndex < BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
  269. Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
  270. if (EFI_ERROR (Status)) {
  271. goto EXIT;
  272. }
  273. }
  274. }
  275. EXIT:
  276. if (!EfiAtRuntime ()) {
  277. // Interruptions can resume.
  278. gBS->RestoreTPL (OriginalTPL);
  279. }
  280. if (EFI_ERROR (Status)) {
  281. DEBUG ((DEBUG_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
  282. }
  283. return Status;
  284. }
  285. EFI_STATUS
  286. EFIAPI
  287. NorFlashInitialise (
  288. IN EFI_HANDLE ImageHandle,
  289. IN EFI_SYSTEM_TABLE *SystemTable
  290. )
  291. {
  292. EFI_STATUS Status;
  293. UINT32 Index;
  294. NOR_FLASH_DESCRIPTION *NorFlashDevices;
  295. BOOLEAN ContainVariableStorage;
  296. Status = NorFlashPlatformInitialization ();
  297. if (EFI_ERROR (Status)) {
  298. DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
  299. return Status;
  300. }
  301. Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
  302. if (EFI_ERROR (Status)) {
  303. DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to get Nor Flash devices\n"));
  304. return Status;
  305. }
  306. mNorFlashInstances = AllocateRuntimePool (sizeof (NOR_FLASH_INSTANCE *) * mNorFlashDeviceCount);
  307. for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
  308. // Check if this NOR Flash device contain the variable storage region
  309. if (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) {
  310. ContainVariableStorage =
  311. (NorFlashDevices[Index].RegionBaseAddress <= PcdGet64 (PcdFlashNvStorageVariableBase64)) &&
  312. (PcdGet64 (PcdFlashNvStorageVariableBase64) + PcdGet32 (PcdFlashNvStorageVariableSize) <=
  313. NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
  314. } else {
  315. ContainVariableStorage =
  316. (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
  317. (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <=
  318. NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
  319. }
  320. Status = NorFlashCreateInstance (
  321. NorFlashDevices[Index].DeviceBaseAddress,
  322. NorFlashDevices[Index].RegionBaseAddress,
  323. NorFlashDevices[Index].Size,
  324. Index,
  325. NorFlashDevices[Index].BlockSize,
  326. ContainVariableStorage,
  327. &mNorFlashInstances[Index]
  328. );
  329. if (EFI_ERROR (Status)) {
  330. DEBUG ((DEBUG_ERROR, "NorFlashInitialise: Fail to create instance for NorFlash[%d]\n", Index));
  331. }
  332. }
  333. //
  334. // Register for the virtual address change event
  335. //
  336. Status = gBS->CreateEventEx (
  337. EVT_NOTIFY_SIGNAL,
  338. TPL_NOTIFY,
  339. NorFlashVirtualNotifyEvent,
  340. NULL,
  341. &gEfiEventVirtualAddressChangeGuid,
  342. &mNorFlashVirtualAddrChangeEvent
  343. );
  344. ASSERT_EFI_ERROR (Status);
  345. return Status;
  346. }
  347. EFI_STATUS
  348. EFIAPI
  349. NorFlashFvbInitialize (
  350. IN NOR_FLASH_INSTANCE *Instance
  351. )
  352. {
  353. EFI_STATUS Status;
  354. UINT32 FvbNumLba;
  355. EFI_BOOT_MODE BootMode;
  356. UINTN RuntimeMmioRegionSize;
  357. DEBUG ((DEBUG_BLKIO, "NorFlashFvbInitialize\n"));
  358. ASSERT ((Instance != NULL));
  359. //
  360. // Declare the Non-Volatile storage as EFI_MEMORY_RUNTIME
  361. //
  362. // Note: all the NOR Flash region needs to be reserved into the UEFI Runtime memory;
  363. // even if we only use the small block region at the top of the NOR Flash.
  364. // The reason is when the NOR Flash memory is set into program mode, the command
  365. // is written as the base of the flash region (ie: Instance->DeviceBaseAddress)
  366. RuntimeMmioRegionSize = (Instance->RegionBaseAddress - Instance->DeviceBaseAddress) + Instance->Size;
  367. Status = gDS->AddMemorySpace (
  368. EfiGcdMemoryTypeMemoryMappedIo,
  369. Instance->DeviceBaseAddress,
  370. RuntimeMmioRegionSize,
  371. EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
  372. );
  373. ASSERT_EFI_ERROR (Status);
  374. Status = gDS->SetMemorySpaceAttributes (
  375. Instance->DeviceBaseAddress,
  376. RuntimeMmioRegionSize,
  377. EFI_MEMORY_UC | EFI_MEMORY_RUNTIME
  378. );
  379. ASSERT_EFI_ERROR (Status);
  380. mFlashNvStorageVariableBase = (PcdGet64 (PcdFlashNvStorageVariableBase64) != 0) ?
  381. PcdGet64 (PcdFlashNvStorageVariableBase64) : PcdGet32 (PcdFlashNvStorageVariableBase);
  382. // Set the index of the first LBA for the FVB
  383. Instance->StartLba = (mFlashNvStorageVariableBase - Instance->RegionBaseAddress) / Instance->Media.BlockSize;
  384. BootMode = GetBootModeHob ();
  385. if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
  386. Status = EFI_INVALID_PARAMETER;
  387. } else {
  388. // Determine if there is a valid header at the beginning of the NorFlash
  389. Status = ValidateFvHeader (Instance);
  390. }
  391. // Install the Default FVB header if required
  392. if (EFI_ERROR (Status)) {
  393. // There is no valid header, so time to install one.
  394. DEBUG ((DEBUG_INFO, "%a: The FVB Header is not valid.\n", __FUNCTION__));
  395. DEBUG ((
  396. DEBUG_INFO,
  397. "%a: Installing a correct one for this volume.\n",
  398. __FUNCTION__
  399. ));
  400. // Erase all the NorFlash that is reserved for variable storage
  401. FvbNumLba = (PcdGet32 (PcdFlashNvStorageVariableSize) + PcdGet32 (PcdFlashNvStorageFtwWorkingSize) + PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / Instance->Media.BlockSize;
  402. Status = FvbEraseBlocks (&Instance->FvbProtocol, (EFI_LBA)0, FvbNumLba, EFI_LBA_LIST_TERMINATOR);
  403. if (EFI_ERROR (Status)) {
  404. return Status;
  405. }
  406. // Install all appropriate headers
  407. Status = InitializeFvAndVariableStoreHeaders (Instance);
  408. if (EFI_ERROR (Status)) {
  409. return Status;
  410. }
  411. }
  412. //
  413. // The driver implementing the variable read service can now be dispatched;
  414. // the varstore headers are in place.
  415. //
  416. Status = gBS->InstallProtocolInterface (
  417. &gImageHandle,
  418. &gEdkiiNvVarStoreFormattedGuid,
  419. EFI_NATIVE_INTERFACE,
  420. NULL
  421. );
  422. ASSERT_EFI_ERROR (Status);
  423. //
  424. // Register for the virtual address change event
  425. //
  426. Status = gBS->CreateEventEx (
  427. EVT_NOTIFY_SIGNAL,
  428. TPL_NOTIFY,
  429. FvbVirtualNotifyEvent,
  430. NULL,
  431. &gEfiEventVirtualAddressChangeGuid,
  432. &mFvbVirtualAddrChangeEvent
  433. );
  434. ASSERT_EFI_ERROR (Status);
  435. return Status;
  436. }