• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file  NorFlashDxe.c
2 
3   Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
4 
5   This program and the accompanying materials
6   are licensed and made available under the terms and conditions of the BSD License
7   which accompanies this distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include <Library/UefiLib.h>
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/MemoryAllocationLib.h>
18 #include <Library/UefiBootServicesTableLib.h>
19 #include <Library/PcdLib.h>
20 
21 #include "NorFlashDxe.h"
22 
23 STATIC EFI_EVENT mNorFlashVirtualAddrChangeEvent;
24 
25 //
26 // Global variable declarations
27 //
28 NOR_FLASH_INSTANCE **mNorFlashInstances;
29 UINT32               mNorFlashDeviceCount;
30 
31 NOR_FLASH_INSTANCE  mNorFlashInstanceTemplate = {
32   NOR_FLASH_SIGNATURE, // Signature
33   NULL, // Handle ... NEED TO BE FILLED
34 
35   FALSE, // Initialized
36   NULL, // Initialize
37 
38   0, // DeviceBaseAddress ... NEED TO BE FILLED
39   0, // RegionBaseAddress ... NEED TO BE FILLED
40   0, // Size ... NEED TO BE FILLED
41   0, // StartLba
42 
43   {
44     EFI_BLOCK_IO_PROTOCOL_REVISION2, // Revision
45     NULL, // Media ... NEED TO BE FILLED
46     NorFlashBlockIoReset, // Reset;
47     NorFlashBlockIoReadBlocks,          // ReadBlocks
48     NorFlashBlockIoWriteBlocks,         // WriteBlocks
49     NorFlashBlockIoFlushBlocks          // FlushBlocks
50   }, // BlockIoProtocol
51 
52   {
53     0, // MediaId ... NEED TO BE FILLED
54     FALSE, // RemovableMedia
55     TRUE, // MediaPresent
56     FALSE, // LogicalPartition
57     FALSE, // ReadOnly
58     FALSE, // WriteCaching;
59     0, // BlockSize ... NEED TO BE FILLED
60     4, //  IoAlign
61     0, // LastBlock ... NEED TO BE FILLED
62     0, // LowestAlignedLba
63     1, // LogicalBlocksPerPhysicalBlock
64   }, //Media;
65 
66   {
67     EFI_DISK_IO_PROTOCOL_REVISION, // Revision
68     NorFlashDiskIoReadDisk,        // ReadDisk
69     NorFlashDiskIoWriteDisk        // WriteDisk
70   },
71 
72   FALSE, // SupportFvb ... NEED TO BE FILLED
73   {
74     FvbGetAttributes, // GetAttributes
75     FvbSetAttributes, // SetAttributes
76     FvbGetPhysicalAddress,  // GetPhysicalAddress
77     FvbGetBlockSize,  // GetBlockSize
78     FvbRead,  // Read
79     FvbWrite, // Write
80     FvbEraseBlocks, // EraseBlocks
81     NULL, //ParentHandle
82   }, //  FvbProtoccol;
83   NULL, // ShadowBuffer
84   {
85     {
86       {
87         HARDWARE_DEVICE_PATH,
88         HW_VENDOR_DP,
89         { (UINT8)sizeof(VENDOR_DEVICE_PATH), (UINT8)((sizeof(VENDOR_DEVICE_PATH)) >> 8) }
90       },
91       { 0x0, 0x0, 0x0, { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 } }, // GUID ... NEED TO BE FILLED
92     },
93     {
94       END_DEVICE_PATH_TYPE,
95       END_ENTIRE_DEVICE_PATH_SUBTYPE,
96       { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 }
97     }
98     } // DevicePath
99 };
100 
101 EFI_STATUS
NorFlashCreateInstance(IN UINTN NorFlashDeviceBase,IN UINTN NorFlashRegionBase,IN UINTN NorFlashSize,IN UINT32 MediaId,IN UINT32 BlockSize,IN BOOLEAN SupportFvb,IN CONST GUID * NorFlashGuid,OUT NOR_FLASH_INSTANCE ** NorFlashInstance)102 NorFlashCreateInstance (
103   IN UINTN                  NorFlashDeviceBase,
104   IN UINTN                  NorFlashRegionBase,
105   IN UINTN                  NorFlashSize,
106   IN UINT32                 MediaId,
107   IN UINT32                 BlockSize,
108   IN BOOLEAN                SupportFvb,
109   IN CONST GUID             *NorFlashGuid,
110   OUT NOR_FLASH_INSTANCE**  NorFlashInstance
111   )
112 {
113   EFI_STATUS Status;
114   NOR_FLASH_INSTANCE* Instance;
115 
116   ASSERT(NorFlashInstance != NULL);
117 
118   Instance = AllocateRuntimeCopyPool (sizeof(NOR_FLASH_INSTANCE),&mNorFlashInstanceTemplate);
119   if (Instance == NULL) {
120     return EFI_OUT_OF_RESOURCES;
121   }
122 
123   Instance->DeviceBaseAddress = NorFlashDeviceBase;
124   Instance->RegionBaseAddress = NorFlashRegionBase;
125   Instance->Size = NorFlashSize;
126 
127   Instance->BlockIoProtocol.Media = &Instance->Media;
128   Instance->Media.MediaId = MediaId;
129   Instance->Media.BlockSize = BlockSize;
130   Instance->Media.LastBlock = (NorFlashSize / BlockSize)-1;
131 
132   CopyGuid (&Instance->DevicePath.Vendor.Guid, NorFlashGuid);
133 
134   Instance->ShadowBuffer = AllocateRuntimePool (BlockSize);;
135   if (Instance->ShadowBuffer == NULL) {
136     return EFI_OUT_OF_RESOURCES;
137   }
138 
139   if (SupportFvb) {
140     Instance->SupportFvb = TRUE;
141     Instance->Initialize = NorFlashFvbInitialize;
142 
143     Status = gBS->InstallMultipleProtocolInterfaces (
144                   &Instance->Handle,
145                   &gEfiDevicePathProtocolGuid, &Instance->DevicePath,
146                   &gEfiBlockIoProtocolGuid,  &Instance->BlockIoProtocol,
147                   &gEfiFirmwareVolumeBlockProtocolGuid, &Instance->FvbProtocol,
148                   NULL
149                   );
150     if (EFI_ERROR(Status)) {
151       FreePool (Instance);
152       return Status;
153     }
154   } else {
155     Instance->Initialized = TRUE;
156 
157     Status = gBS->InstallMultipleProtocolInterfaces (
158                     &Instance->Handle,
159                     &gEfiDevicePathProtocolGuid, &Instance->DevicePath,
160                     &gEfiBlockIoProtocolGuid,  &Instance->BlockIoProtocol,
161                     &gEfiDiskIoProtocolGuid, &Instance->DiskIoProtocol,
162                     NULL
163                     );
164     if (EFI_ERROR(Status)) {
165       FreePool (Instance);
166       return Status;
167     }
168   }
169 
170   *NorFlashInstance = Instance;
171   return Status;
172 }
173 
174 UINT32
NorFlashReadStatusRegister(IN NOR_FLASH_INSTANCE * Instance,IN UINTN SR_Address)175 NorFlashReadStatusRegister (
176   IN NOR_FLASH_INSTANCE     *Instance,
177   IN UINTN                  SR_Address
178   )
179 {
180   // Prepare to read the status register
181   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
182   return MmioRead32 (Instance->DeviceBaseAddress);
183 }
184 
185 STATIC
186 BOOLEAN
NorFlashBlockIsLocked(IN NOR_FLASH_INSTANCE * Instance,IN UINTN BlockAddress)187 NorFlashBlockIsLocked (
188   IN NOR_FLASH_INSTANCE     *Instance,
189   IN UINTN                  BlockAddress
190   )
191 {
192   UINT32                LockStatus;
193 
194   // Send command for reading device id
195   SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
196 
197   // Read block lock status
198   LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));
199 
200   // Decode block lock status
201   LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);
202 
203   if ((LockStatus & 0x2) != 0) {
204     DEBUG((EFI_D_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
205   }
206 
207   return ((LockStatus & 0x1) != 0);
208 }
209 
210 STATIC
211 EFI_STATUS
NorFlashUnlockSingleBlock(IN NOR_FLASH_INSTANCE * Instance,IN UINTN BlockAddress)212 NorFlashUnlockSingleBlock (
213   IN NOR_FLASH_INSTANCE     *Instance,
214   IN UINTN                  BlockAddress
215   )
216 {
217   UINT32                LockStatus;
218 
219   // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
220   // and to protect shared data structures.
221 
222   if (FeaturePcdGet (PcdNorFlashCheckBlockLocked) == TRUE) {
223     do {
224       // Request a lock setup
225       SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
226 
227       // Request an unlock
228       SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
229 
230       // Send command for reading device id
231       SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
232 
233       // Read block lock status
234       LockStatus = MmioRead32 (CREATE_NOR_ADDRESS(BlockAddress, 2));
235 
236       // Decode block lock status
237       LockStatus = FOLD_32BIT_INTO_16BIT(LockStatus);
238     } while ((LockStatus & 0x1) == 1);
239   } else {
240     // Request a lock setup
241     SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
242 
243     // Request an unlock
244     SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
245 
246     // Wait until the status register gives us the all clear
247     do {
248       LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
249     } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
250   }
251 
252   // Put device back into Read Array mode
253   SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);
254 
255   DEBUG((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress));
256 
257   return EFI_SUCCESS;
258 }
259 
260 STATIC
261 EFI_STATUS
NorFlashUnlockSingleBlockIfNecessary(IN NOR_FLASH_INSTANCE * Instance,IN UINTN BlockAddress)262 NorFlashUnlockSingleBlockIfNecessary (
263   IN NOR_FLASH_INSTANCE     *Instance,
264   IN UINTN                  BlockAddress
265   )
266 {
267   EFI_STATUS Status;
268 
269   Status = EFI_SUCCESS;
270 
271   if (NorFlashBlockIsLocked (Instance, BlockAddress) == TRUE) {
272     Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
273   }
274 
275   return Status;
276 }
277 
278 
279 /**
280  * The following function presumes that the block has already been unlocked.
281  **/
282 STATIC
283 EFI_STATUS
NorFlashEraseSingleBlock(IN NOR_FLASH_INSTANCE * Instance,IN UINTN BlockAddress)284 NorFlashEraseSingleBlock (
285   IN NOR_FLASH_INSTANCE     *Instance,
286   IN UINTN                  BlockAddress
287   )
288 {
289   EFI_STATUS            Status;
290   UINT32                StatusRegister;
291 
292   Status = EFI_SUCCESS;
293 
294   // Request a block erase and then confirm it
295   SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);
296   SEND_NOR_COMMAND(BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);
297 
298   // Wait until the status register gives us the all clear
299   do {
300     StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
301   } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
302 
303   if (StatusRegister & P30_SR_BIT_VPP) {
304     DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));
305     Status = EFI_DEVICE_ERROR;
306   }
307 
308   if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {
309     DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));
310     Status = EFI_DEVICE_ERROR;
311   }
312 
313   if (StatusRegister & P30_SR_BIT_ERASE) {
314     DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));
315     Status = EFI_DEVICE_ERROR;
316   }
317 
318   if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
319     // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
320     DEBUG((EFI_D_INFO,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));
321     Status = EFI_WRITE_PROTECTED;
322   }
323 
324   if (EFI_ERROR(Status)) {
325     // Clear the Status Register
326     SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
327   }
328 
329   // Put device back into Read Array mode
330   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
331 
332   return Status;
333 }
334 
335 /**
336  * This function unlock and erase an entire NOR Flash block.
337  **/
338 EFI_STATUS
NorFlashUnlockAndEraseSingleBlock(IN NOR_FLASH_INSTANCE * Instance,IN UINTN BlockAddress)339 NorFlashUnlockAndEraseSingleBlock (
340   IN NOR_FLASH_INSTANCE     *Instance,
341   IN UINTN                  BlockAddress
342   )
343 {
344   EFI_STATUS      Status;
345   UINTN           Index;
346   EFI_TPL         OriginalTPL;
347 
348   if (!EfiAtRuntime ()) {
349     // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
350     OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
351   } else {
352     // This initialization is only to prevent the compiler to complain about the
353     // use of uninitialized variables
354     OriginalTPL = TPL_HIGH_LEVEL;
355   }
356 
357   Index = 0;
358   // The block erase might fail a first time (SW bug ?). Retry it ...
359   do {
360     // Unlock the block if we have to
361     Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
362     if (EFI_ERROR (Status)) {
363       break;
364     }
365     Status = NorFlashEraseSingleBlock (Instance, BlockAddress);
366     Index++;
367   } while ((Index < NOR_FLASH_ERASE_RETRY) && (Status == EFI_WRITE_PROTECTED));
368 
369   if (Index == NOR_FLASH_ERASE_RETRY) {
370     DEBUG((EFI_D_ERROR,"EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error (try to erase %d times)\n", BlockAddress,Index));
371   }
372 
373   if (!EfiAtRuntime ()) {
374     // Interruptions can resume.
375     gBS->RestoreTPL (OriginalTPL);
376   }
377 
378   return Status;
379 }
380 
381 
382 STATIC
383 EFI_STATUS
NorFlashWriteSingleWord(IN NOR_FLASH_INSTANCE * Instance,IN UINTN WordAddress,IN UINT32 WriteData)384 NorFlashWriteSingleWord (
385   IN NOR_FLASH_INSTANCE     *Instance,
386   IN UINTN                  WordAddress,
387   IN UINT32                 WriteData
388   )
389 {
390   EFI_STATUS            Status;
391   UINT32                StatusRegister;
392 
393   Status = EFI_SUCCESS;
394 
395   // Request a write single word command
396   SEND_NOR_COMMAND(WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
397 
398   // Store the word into NOR Flash;
399   MmioWrite32 (WordAddress, WriteData);
400 
401   // Wait for the write to complete and then check for any errors; i.e. check the Status Register
402   do {
403     // Prepare to read the status register
404     StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
405     // The chip is busy while the WRITE bit is not asserted
406   } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
407 
408 
409   // Perform a full status check:
410   // Mask the relevant bits of Status Register.
411   // Everything should be zero, if not, we have a problem
412 
413   if (StatusRegister & P30_SR_BIT_VPP) {
414     DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n",WordAddress));
415     Status = EFI_DEVICE_ERROR;
416   }
417 
418   if (StatusRegister & P30_SR_BIT_PROGRAM) {
419     DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n",WordAddress));
420     Status = EFI_DEVICE_ERROR;
421   }
422 
423   if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
424     DEBUG((EFI_D_ERROR,"NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n",WordAddress));
425     Status = EFI_DEVICE_ERROR;
426   }
427 
428   if (!EFI_ERROR(Status)) {
429     // Clear the Status Register
430     SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
431   }
432 
433   // Put device back into Read Array mode
434   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
435 
436   return Status;
437 }
438 
439 /*
440  * Writes data to the NOR Flash using the Buffered Programming method.
441  *
442  * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
443  * Therefore this function will only handle buffers up to 32 words or 128 bytes.
444  * To deal with larger buffers, call this function again.
445  *
446  * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
447  * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
448  *
449  * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
450  * then programming time is doubled and power consumption is increased.
451  * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
452  * i.e. the last 4 bits of the target start address must be zero: 0x......00
453  */
454 EFI_STATUS
NorFlashWriteBuffer(IN NOR_FLASH_INSTANCE * Instance,IN UINTN TargetAddress,IN UINTN BufferSizeInBytes,IN UINT32 * Buffer)455 NorFlashWriteBuffer (
456   IN NOR_FLASH_INSTANCE     *Instance,
457   IN UINTN                  TargetAddress,
458   IN UINTN                  BufferSizeInBytes,
459   IN UINT32                 *Buffer
460   )
461 {
462   EFI_STATUS            Status;
463   UINTN                 BufferSizeInWords;
464   UINTN                 Count;
465   volatile UINT32       *Data;
466   UINTN                 WaitForBuffer;
467   BOOLEAN               BufferAvailable;
468   UINT32                StatusRegister;
469 
470   WaitForBuffer   = MAX_BUFFERED_PROG_ITERATIONS;
471   BufferAvailable = FALSE;
472 
473   // Check that the target address does not cross a 32-word boundary.
474   if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {
475     return EFI_INVALID_PARAMETER;
476   }
477 
478   // Check there are some data to program
479   if (BufferSizeInBytes == 0) {
480     return EFI_BUFFER_TOO_SMALL;
481   }
482 
483   // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
484   if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {
485     return EFI_BAD_BUFFER_SIZE;
486   }
487 
488   // Check that the buffer size is a multiple of 32-bit words
489   if ((BufferSizeInBytes % 4) != 0) {
490     return EFI_BAD_BUFFER_SIZE;
491   }
492 
493   // Pre-programming conditions checked, now start the algorithm.
494 
495   // Prepare the data destination address
496   Data = (UINT32 *)TargetAddress;
497 
498   // Check the availability of the buffer
499   do {
500     // Issue the Buffered Program Setup command
501     SEND_NOR_COMMAND(TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);
502 
503     // Read back the status register bit#7 from the same address
504     if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {
505       BufferAvailable = TRUE;
506     }
507 
508     // Update the loop counter
509     WaitForBuffer--;
510 
511   } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));
512 
513   // The buffer was not available for writing
514   if (WaitForBuffer == 0) {
515     Status = EFI_DEVICE_ERROR;
516     goto EXIT;
517   }
518 
519   // From now on we work in 32-bit words
520   BufferSizeInWords = BufferSizeInBytes / (UINTN)4;
521 
522   // Write the word count, which is (buffer_size_in_words - 1),
523   // because word count 0 means one word.
524   SEND_NOR_COMMAND(TargetAddress, 0, (BufferSizeInWords - 1));
525 
526   // Write the data to the NOR Flash, advancing each address by 4 bytes
527   for(Count=0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {
528     MmioWrite32 ((UINTN)Data, *Buffer);
529   }
530 
531   // Issue the Buffered Program Confirm command, to start the programming operation
532   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
533 
534   // Wait for the write to complete and then check for any errors; i.e. check the Status Register
535   do {
536     StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);
537     // The chip is busy while the WRITE bit is not asserted
538   } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
539 
540 
541   // Perform a full status check:
542   // Mask the relevant bits of Status Register.
543   // Everything should be zero, if not, we have a problem
544 
545   Status          = EFI_SUCCESS;
546 
547   if (StatusRegister & P30_SR_BIT_VPP) {
548     DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));
549     Status = EFI_DEVICE_ERROR;
550   }
551 
552   if (StatusRegister & P30_SR_BIT_PROGRAM) {
553     DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));
554     Status = EFI_DEVICE_ERROR;
555   }
556 
557   if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
558     DEBUG((EFI_D_ERROR,"NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n",TargetAddress));
559     Status = EFI_DEVICE_ERROR;
560   }
561 
562   if (!EFI_ERROR(Status)) {
563     // Clear the Status Register
564     SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
565   }
566 
567 EXIT:
568   // Put device back into Read Array mode
569   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
570 
571   return Status;
572 }
573 
574 STATIC
575 EFI_STATUS
NorFlashWriteFullBlock(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINT32 * DataBuffer,IN UINT32 BlockSizeInWords)576 NorFlashWriteFullBlock (
577   IN NOR_FLASH_INSTANCE     *Instance,
578   IN EFI_LBA                Lba,
579   IN UINT32                 *DataBuffer,
580   IN UINT32                 BlockSizeInWords
581   )
582 {
583   EFI_STATUS    Status;
584   UINTN         WordAddress;
585   UINT32        WordIndex;
586   UINTN         BufferIndex;
587   UINTN         BlockAddress;
588   UINTN         BuffersInBlock;
589   UINTN         RemainingWords;
590   EFI_TPL       OriginalTPL;
591   UINTN         Cnt;
592 
593   Status = EFI_SUCCESS;
594 
595   // Get the physical address of the block
596   BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSizeInWords * 4);
597 
598   // Start writing from the first address at the start of the block
599   WordAddress = BlockAddress;
600 
601   if (!EfiAtRuntime ()) {
602     // Raise TPL to TPL_HIGH to stop anyone from interrupting us.
603     OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
604   } else {
605     // This initialization is only to prevent the compiler to complain about the
606     // use of uninitialized variables
607     OriginalTPL = TPL_HIGH_LEVEL;
608   }
609 
610   Status = NorFlashUnlockAndEraseSingleBlock (Instance, BlockAddress);
611   if (EFI_ERROR(Status)) {
612     DEBUG((EFI_D_ERROR, "WriteSingleBlock: ERROR - Failed to Unlock and Erase the single block at 0x%X\n", BlockAddress));
613     goto EXIT;
614   }
615 
616   // To speed up the programming operation, NOR Flash is programmed using the Buffered Programming method.
617 
618   // Check that the address starts at a 32-word boundary, i.e. last 7 bits must be zero
619   if ((WordAddress & BOUNDARY_OF_32_WORDS) == 0x00) {
620 
621     // First, break the entire block into buffer-sized chunks.
622     BuffersInBlock = (UINTN)(BlockSizeInWords * 4) / P30_MAX_BUFFER_SIZE_IN_BYTES;
623 
624     // Then feed each buffer chunk to the NOR Flash
625     // If a buffer does not contain any data, don't write it.
626     for(BufferIndex=0;
627          BufferIndex < BuffersInBlock;
628          BufferIndex++, WordAddress += P30_MAX_BUFFER_SIZE_IN_BYTES, DataBuffer += P30_MAX_BUFFER_SIZE_IN_WORDS
629       ) {
630       // Check the buffer to see if it contains any data (not set all 1s).
631       for (Cnt = 0; Cnt < P30_MAX_BUFFER_SIZE_IN_WORDS; Cnt++) {
632         if (~DataBuffer[Cnt] != 0 ) {
633           // Some data found, write the buffer.
634           Status = NorFlashWriteBuffer (Instance, WordAddress, P30_MAX_BUFFER_SIZE_IN_BYTES,
635                                         DataBuffer);
636           if (EFI_ERROR(Status)) {
637             goto EXIT;
638           }
639           break;
640         }
641       }
642     }
643 
644     // Finally, finish off any remaining words that are less than the maximum size of the buffer
645     RemainingWords = BlockSizeInWords % P30_MAX_BUFFER_SIZE_IN_WORDS;
646 
647     if(RemainingWords != 0) {
648       Status = NorFlashWriteBuffer (Instance, WordAddress, (RemainingWords * 4), DataBuffer);
649       if (EFI_ERROR(Status)) {
650         goto EXIT;
651       }
652     }
653 
654   } else {
655     // For now, use the single word programming algorithm
656     // It is unlikely that the NOR Flash will exist in an address which falls within a 32 word boundary range,
657     // i.e. which ends in the range 0x......01 - 0x......7F.
658     for(WordIndex=0; WordIndex<BlockSizeInWords; WordIndex++, DataBuffer++, WordAddress = WordAddress + 4) {
659       Status = NorFlashWriteSingleWord (Instance, WordAddress, *DataBuffer);
660       if (EFI_ERROR(Status)) {
661         goto EXIT;
662       }
663     }
664   }
665 
666 EXIT:
667   if (!EfiAtRuntime ()) {
668     // Interruptions can resume.
669     gBS->RestoreTPL (OriginalTPL);
670   }
671 
672   if (EFI_ERROR(Status)) {
673     DEBUG((EFI_D_ERROR, "NOR FLASH Programming [WriteSingleBlock] failed at address 0x%08x. Exit Status = \"%r\".\n", WordAddress, Status));
674   }
675   return Status;
676 }
677 
678 
679 EFI_STATUS
NorFlashWriteBlocks(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN BufferSizeInBytes,IN VOID * Buffer)680 NorFlashWriteBlocks (
681   IN NOR_FLASH_INSTANCE     *Instance,
682   IN EFI_LBA                Lba,
683   IN UINTN                  BufferSizeInBytes,
684   IN VOID                   *Buffer
685   )
686 {
687   UINT32          *pWriteBuffer;
688   EFI_STATUS      Status = EFI_SUCCESS;
689   EFI_LBA         CurrentBlock;
690   UINT32          BlockSizeInWords;
691   UINT32          NumBlocks;
692   UINT32          BlockCount;
693 
694   // The buffer must be valid
695   if (Buffer == NULL) {
696     return EFI_INVALID_PARAMETER;
697   }
698 
699   if(Instance->Media.ReadOnly == TRUE) {
700     return EFI_WRITE_PROTECTED;
701   }
702 
703   // We must have some bytes to read
704   DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
705   if(BufferSizeInBytes == 0) {
706     return EFI_BAD_BUFFER_SIZE;
707   }
708 
709   // The size of the buffer must be a multiple of the block size
710   DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->Media.BlockSize));
711   if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
712     return EFI_BAD_BUFFER_SIZE;
713   }
714 
715   // All blocks must be within the device
716   NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
717 
718   DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->Media.LastBlock, Lba));
719 
720   if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
721     DEBUG((EFI_D_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
722     return EFI_INVALID_PARAMETER;
723   }
724 
725   BlockSizeInWords = Instance->Media.BlockSize / 4;
726 
727   // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
728   // to a proper data type, so use *ReadBuffer
729   pWriteBuffer = (UINT32 *)Buffer;
730 
731   CurrentBlock = Lba;
732   for (BlockCount=0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
733 
734     DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
735 
736     Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
737 
738     if (EFI_ERROR(Status)) {
739       break;
740     }
741   }
742 
743   DEBUG((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
744   return Status;
745 }
746 
747 #define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0)
748 
749 /**
750   Copy Length bytes from Source to Destination, using aligned accesses only.
751   Note that this implementation uses memcpy() semantics rather then memmove()
752   semantics, i.e., SourceBuffer and DestinationBuffer should not overlap.
753 
754   @param  DestinationBuffer The target of the copy request.
755   @param  SourceBuffer      The place to copy from.
756   @param  Length            The number of bytes to copy.
757 
758   @return Destination
759 
760 **/
761 STATIC
762 VOID *
AlignedCopyMem(OUT VOID * DestinationBuffer,IN CONST VOID * SourceBuffer,IN UINTN Length)763 AlignedCopyMem (
764   OUT     VOID                      *DestinationBuffer,
765   IN      CONST VOID                *SourceBuffer,
766   IN      UINTN                     Length
767   )
768 {
769   UINT8             *Destination8;
770   CONST UINT8       *Source8;
771   UINT32            *Destination32;
772   CONST UINT32      *Source32;
773   UINT64            *Destination64;
774   CONST UINT64      *Source64;
775 
776   if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 8) && Length >= 8) {
777     Destination64 = DestinationBuffer;
778     Source64 = SourceBuffer;
779     while (Length >= 8) {
780       *Destination64++ = *Source64++;
781       Length -= 8;
782     }
783 
784     Destination8 = (UINT8 *)Destination64;
785     Source8 = (CONST UINT8 *)Source64;
786   } else if (BOTH_ALIGNED(DestinationBuffer, SourceBuffer, 4) && Length >= 4) {
787     Destination32 = DestinationBuffer;
788     Source32 = SourceBuffer;
789     while (Length >= 4) {
790       *Destination32++ = *Source32++;
791       Length -= 4;
792     }
793 
794     Destination8 = (UINT8 *)Destination32;
795     Source8 = (CONST UINT8 *)Source32;
796   } else {
797     Destination8 = DestinationBuffer;
798     Source8 = SourceBuffer;
799   }
800   while (Length-- != 0) {
801     *Destination8++ = *Source8++;
802   }
803   return DestinationBuffer;
804 }
805 
806 EFI_STATUS
NorFlashReadBlocks(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN BufferSizeInBytes,OUT VOID * Buffer)807 NorFlashReadBlocks (
808   IN NOR_FLASH_INSTANCE   *Instance,
809   IN EFI_LBA              Lba,
810   IN UINTN                BufferSizeInBytes,
811   OUT VOID                *Buffer
812   )
813 {
814   UINT32              NumBlocks;
815   UINTN               StartAddress;
816 
817   DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
818       BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));
819 
820   // The buffer must be valid
821   if (Buffer == NULL) {
822     return EFI_INVALID_PARAMETER;
823   }
824 
825   // Return if we have not any byte to read
826   if (BufferSizeInBytes == 0) {
827     return EFI_SUCCESS;
828   }
829 
830   // The size of the buffer must be a multiple of the block size
831   if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
832     return EFI_BAD_BUFFER_SIZE;
833   }
834 
835   // All blocks must be within the device
836   NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
837 
838   if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
839     DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
840     return EFI_INVALID_PARAMETER;
841   }
842 
843   // Get the address to start reading from
844   StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
845                                         Lba,
846                                         Instance->Media.BlockSize
847                                        );
848 
849   // Put the device into Read Array mode
850   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
851 
852   // Readout the data
853   AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
854 
855   return EFI_SUCCESS;
856 }
857 
858 EFI_STATUS
NorFlashRead(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN BufferSizeInBytes,OUT VOID * Buffer)859 NorFlashRead (
860   IN NOR_FLASH_INSTANCE   *Instance,
861   IN EFI_LBA              Lba,
862   IN UINTN                Offset,
863   IN UINTN                BufferSizeInBytes,
864   OUT VOID                *Buffer
865   )
866 {
867   UINTN  StartAddress;
868 
869   // The buffer must be valid
870   if (Buffer == NULL) {
871     return EFI_INVALID_PARAMETER;
872   }
873 
874   // Return if we have not any byte to read
875   if (BufferSizeInBytes == 0) {
876     return EFI_SUCCESS;
877   }
878 
879   if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
880     DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
881     return EFI_INVALID_PARAMETER;
882   }
883 
884   // Get the address to start reading from
885   StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
886                                         Lba,
887                                         Instance->Media.BlockSize
888                                        );
889 
890   // Put the device into Read Array mode
891   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
892 
893   // Readout the data
894   AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
895 
896   return EFI_SUCCESS;
897 }
898 
899 /*
900   Write a full or portion of a block. It must not span block boundaries; that is,
901   Offset + *NumBytes <= Instance->Media.BlockSize.
902 */
903 EFI_STATUS
NorFlashWriteSingleBlock(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)904 NorFlashWriteSingleBlock (
905   IN        NOR_FLASH_INSTANCE   *Instance,
906   IN        EFI_LBA               Lba,
907   IN        UINTN                 Offset,
908   IN OUT    UINTN                *NumBytes,
909   IN        UINT8                *Buffer
910   )
911 {
912   EFI_STATUS  TempStatus;
913   UINT32      Tmp;
914   UINT32      TmpBuf;
915   UINT32      WordToWrite;
916   UINT32      Mask;
917   BOOLEAN     DoErase;
918   UINTN       BytesToWrite;
919   UINTN       CurOffset;
920   UINTN       WordAddr;
921   UINTN       BlockSize;
922   UINTN       BlockAddress;
923   UINTN       PrevBlockAddress;
924 
925   PrevBlockAddress = 0;
926 
927   if (!Instance->Initialized && Instance->Initialize) {
928     Instance->Initialize(Instance);
929   }
930 
931   DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
932 
933   // Detect WriteDisabled state
934   if (Instance->Media.ReadOnly == TRUE) {
935     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
936     // It is in WriteDisabled state, return an error right away
937     return EFI_ACCESS_DENIED;
938   }
939 
940   // Cache the block size to avoid de-referencing pointers all the time
941   BlockSize = Instance->Media.BlockSize;
942 
943   // The write must not span block boundaries.
944   // We need to check each variable individually because adding two large values together overflows.
945   if ( ( Offset               >= BlockSize ) ||
946        ( *NumBytes            >  BlockSize ) ||
947        ( (Offset + *NumBytes) >  BlockSize )    ) {
948     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
949     return EFI_BAD_BUFFER_SIZE;
950   }
951 
952   // We must have some bytes to write
953   if (*NumBytes == 0) {
954     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
955     return EFI_BAD_BUFFER_SIZE;
956   }
957 
958   // Pick 128bytes as a good start for word operations as opposed to erasing the
959   // block and writing the data regardless if an erase is really needed.
960   // It looks like most individual NV variable writes are smaller than 128bytes.
961   if (*NumBytes <= 128) {
962     // Check to see if we need to erase before programming the data into NOR.
963     // If the destination bits are only changing from 1s to 0s we can just write.
964     // After a block is erased all bits in the block is set to 1.
965     // If any byte requires us to erase we just give up and rewrite all of it.
966     DoErase      = FALSE;
967     BytesToWrite = *NumBytes;
968     CurOffset    = Offset;
969 
970     while (BytesToWrite > 0) {
971       // Read full word from NOR, splice as required. A word is the smallest
972       // unit we can write.
973       TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof(Tmp), &Tmp);
974       if (EFI_ERROR (TempStatus)) {
975         return EFI_DEVICE_ERROR;
976       }
977 
978       // Physical address of word in NOR to write.
979       WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
980                                                                Lba, BlockSize);
981       // The word of data that is to be written.
982       TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite)));
983 
984       // First do word aligned chunks.
985       if ((CurOffset & 0x3) == 0) {
986         if (BytesToWrite >= 4) {
987           // Is the destination still in 'erased' state?
988           if (~Tmp != 0) {
989             // Check to see if we are only changing bits to zero.
990             if ((Tmp ^ TmpBuf) & TmpBuf) {
991               DoErase = TRUE;
992               break;
993             }
994           }
995           // Write this word to NOR
996           WordToWrite = TmpBuf;
997           CurOffset += sizeof(TmpBuf);
998           BytesToWrite -= sizeof(TmpBuf);
999         } else {
1000           // BytesToWrite < 4. Do small writes and left-overs
1001           Mask = ~((~0) << (BytesToWrite * 8));
1002           // Mask out the bytes we want.
1003           TmpBuf &= Mask;
1004           // Is the destination still in 'erased' state?
1005           if ((Tmp & Mask) != Mask) {
1006             // Check to see if we are only changing bits to zero.
1007             if ((Tmp ^ TmpBuf) & TmpBuf) {
1008               DoErase = TRUE;
1009               break;
1010             }
1011           }
1012           // Merge old and new data. Write merged word to NOR
1013           WordToWrite = (Tmp & ~Mask) | TmpBuf;
1014           CurOffset += BytesToWrite;
1015           BytesToWrite = 0;
1016         }
1017       } else {
1018         // Do multiple words, but starting unaligned.
1019         if (BytesToWrite > (4 - (CurOffset & 0x3))) {
1020           Mask = ((~0) << ((CurOffset & 0x3) * 8));
1021           // Mask out the bytes we want.
1022           TmpBuf &= Mask;
1023           // Is the destination still in 'erased' state?
1024           if ((Tmp & Mask) != Mask) {
1025             // Check to see if we are only changing bits to zero.
1026             if ((Tmp ^ TmpBuf) & TmpBuf) {
1027               DoErase = TRUE;
1028               break;
1029             }
1030           }
1031           // Merge old and new data. Write merged word to NOR
1032           WordToWrite = (Tmp & ~Mask) | TmpBuf;
1033           BytesToWrite -= (4 - (CurOffset & 0x3));
1034           CurOffset += (4 - (CurOffset & 0x3));
1035         } else {
1036           // Unaligned and fits in one word.
1037           Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
1038           // Mask out the bytes we want.
1039           TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
1040           // Is the destination still in 'erased' state?
1041           if ((Tmp & Mask) != Mask) {
1042             // Check to see if we are only changing bits to zero.
1043             if ((Tmp ^ TmpBuf) & TmpBuf) {
1044               DoErase = TRUE;
1045               break;
1046             }
1047           }
1048           // Merge old and new data. Write merged word to NOR
1049           WordToWrite = (Tmp & ~Mask) | TmpBuf;
1050           CurOffset += BytesToWrite;
1051           BytesToWrite = 0;
1052         }
1053       }
1054 
1055       //
1056       // Write the word to NOR.
1057       //
1058 
1059       BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
1060       if (BlockAddress != PrevBlockAddress) {
1061         TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
1062         if (EFI_ERROR (TempStatus)) {
1063           return EFI_DEVICE_ERROR;
1064         }
1065         PrevBlockAddress = BlockAddress;
1066       }
1067       TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
1068       if (EFI_ERROR (TempStatus)) {
1069         return EFI_DEVICE_ERROR;
1070       }
1071     }
1072     // Exit if we got here and could write all the data. Otherwise do the
1073     // Erase-Write cycle.
1074     if (!DoErase) {
1075       return EFI_SUCCESS;
1076     }
1077   }
1078 
1079   // Check we did get some memory. Buffer is BlockSize.
1080   if (Instance->ShadowBuffer == NULL) {
1081     DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
1082     return EFI_DEVICE_ERROR;
1083   }
1084 
1085   // Read NOR Flash data into shadow buffer
1086   TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
1087   if (EFI_ERROR (TempStatus)) {
1088     // Return one of the pre-approved error statuses
1089     return EFI_DEVICE_ERROR;
1090   }
1091 
1092   // Put the data at the appropriate location inside the buffer area
1093   CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
1094 
1095   // Write the modified buffer back to the NorFlash
1096   TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
1097   if (EFI_ERROR (TempStatus)) {
1098     // Return one of the pre-approved error statuses
1099     return EFI_DEVICE_ERROR;
1100   }
1101 
1102   return EFI_SUCCESS;
1103 }
1104 
1105 /*
1106   Although DiskIoDxe will automatically install the DiskIO protocol whenever
1107   we install the BlockIO protocol, its implementation is sub-optimal as it reads
1108   and writes entire blocks using the BlockIO protocol. In fact we can access
1109   NOR flash with a finer granularity than that, so we can improve performance
1110   by directly producing the DiskIO protocol.
1111 */
1112 
1113 /**
1114   Read BufferSize bytes from Offset into Buffer.
1115 
1116   @param  This                  Protocol instance pointer.
1117   @param  MediaId               Id of the media, changes every time the media is replaced.
1118   @param  Offset                The starting byte offset to read from
1119   @param  BufferSize            Size of Buffer
1120   @param  Buffer                Buffer containing read data
1121 
1122   @retval EFI_SUCCESS           The data was read correctly from the device.
1123   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
1124   @retval EFI_NO_MEDIA          There is no media in the device.
1125   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
1126   @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1127                                 valid for the device.
1128 
1129 **/
1130 EFI_STATUS
1131 EFIAPI
NorFlashDiskIoReadDisk(IN EFI_DISK_IO_PROTOCOL * This,IN UINT32 MediaId,IN UINT64 DiskOffset,IN UINTN BufferSize,OUT VOID * Buffer)1132 NorFlashDiskIoReadDisk (
1133   IN EFI_DISK_IO_PROTOCOL         *This,
1134   IN UINT32                       MediaId,
1135   IN UINT64                       DiskOffset,
1136   IN UINTN                        BufferSize,
1137   OUT VOID                        *Buffer
1138   )
1139 {
1140   NOR_FLASH_INSTANCE *Instance;
1141   UINT32              BlockSize;
1142   UINT32              BlockOffset;
1143   EFI_LBA             Lba;
1144 
1145   Instance = INSTANCE_FROM_DISKIO_THIS(This);
1146 
1147   if (MediaId != Instance->Media.MediaId) {
1148     return EFI_MEDIA_CHANGED;
1149   }
1150 
1151   BlockSize = Instance->Media.BlockSize;
1152   Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1153 
1154   return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
1155 }
1156 
1157 /**
1158   Writes a specified number of bytes to a device.
1159 
1160   @param  This       Indicates a pointer to the calling context.
1161   @param  MediaId    ID of the medium to be written.
1162   @param  Offset     The starting byte offset on the logical block I/O device to write.
1163   @param  BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1164   @param  Buffer     A pointer to the buffer containing the data to be written.
1165 
1166   @retval EFI_SUCCESS           The data was written correctly to the device.
1167   @retval EFI_WRITE_PROTECTED   The device can not be written to.
1168   @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
1169   @retval EFI_NO_MEDIA          There is no media in the device.
1170   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
1171   @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1172                                  valid for the device.
1173 
1174 **/
1175 EFI_STATUS
1176 EFIAPI
NorFlashDiskIoWriteDisk(IN EFI_DISK_IO_PROTOCOL * This,IN UINT32 MediaId,IN UINT64 DiskOffset,IN UINTN BufferSize,IN VOID * Buffer)1177 NorFlashDiskIoWriteDisk (
1178   IN EFI_DISK_IO_PROTOCOL         *This,
1179   IN UINT32                       MediaId,
1180   IN UINT64                       DiskOffset,
1181   IN UINTN                        BufferSize,
1182   IN VOID                         *Buffer
1183   )
1184 {
1185   NOR_FLASH_INSTANCE *Instance;
1186   UINT32              BlockSize;
1187   UINT32              BlockOffset;
1188   EFI_LBA             Lba;
1189   UINTN               RemainingBytes;
1190   UINTN               WriteSize;
1191   EFI_STATUS          Status;
1192 
1193   Instance = INSTANCE_FROM_DISKIO_THIS(This);
1194 
1195   if (MediaId != Instance->Media.MediaId) {
1196     return EFI_MEDIA_CHANGED;
1197   }
1198 
1199   BlockSize = Instance->Media.BlockSize;
1200   Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1201 
1202   RemainingBytes = BufferSize;
1203 
1204   // Write either all the remaining bytes, or the number of bytes that bring
1205   // us up to a block boundary, whichever is less.
1206   // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1207   // block boundary (even if it is already on one).
1208   WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);
1209 
1210   do {
1211     if (WriteSize == BlockSize) {
1212       // Write a full block
1213       Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
1214     } else {
1215       // Write a partial block
1216       Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
1217     }
1218     if (EFI_ERROR (Status)) {
1219       return Status;
1220     }
1221     // Now continue writing either all the remaining bytes or single blocks.
1222     RemainingBytes -= WriteSize;
1223     Buffer = (UINT8 *) Buffer + WriteSize;
1224     Lba++;
1225     BlockOffset = 0;
1226     WriteSize = MIN (RemainingBytes, BlockSize);
1227   } while (RemainingBytes);
1228 
1229   return Status;
1230 }
1231 
1232 EFI_STATUS
NorFlashReset(IN NOR_FLASH_INSTANCE * Instance)1233 NorFlashReset (
1234   IN  NOR_FLASH_INSTANCE *Instance
1235   )
1236 {
1237   // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1238   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
1239   return EFI_SUCCESS;
1240 }
1241 
1242 /**
1243   Fixup internal data so that EFI can be call in virtual mode.
1244   Call the passed in Child Notify event and convert any pointers in
1245   lib to virtual mode.
1246 
1247   @param[in]    Event   The Event that is being processed
1248   @param[in]    Context Event Context
1249 **/
1250 VOID
1251 EFIAPI
NorFlashVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)1252 NorFlashVirtualNotifyEvent (
1253   IN EFI_EVENT        Event,
1254   IN VOID             *Context
1255   )
1256 {
1257   UINTN Index;
1258 
1259   for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1260     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);
1261     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);
1262 
1263     // Convert BlockIo protocol
1264     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
1265     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
1266     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
1267     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
1268 
1269     // Convert Fvb
1270     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
1271     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
1272     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
1273     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
1274     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);
1275     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
1276     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);
1277 
1278     if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
1279       EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer);
1280     }
1281   }
1282 
1283   return;
1284 }
1285 
1286 EFI_STATUS
1287 EFIAPI
NorFlashInitialise(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1288 NorFlashInitialise (
1289   IN EFI_HANDLE         ImageHandle,
1290   IN EFI_SYSTEM_TABLE   *SystemTable
1291   )
1292 {
1293   EFI_STATUS              Status;
1294   UINT32                  Index;
1295   NOR_FLASH_DESCRIPTION*  NorFlashDevices;
1296   BOOLEAN                 ContainVariableStorage;
1297 
1298   Status = NorFlashPlatformInitialization ();
1299   if (EFI_ERROR(Status)) {
1300     DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1301     return Status;
1302   }
1303 
1304   Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
1305   if (EFI_ERROR(Status)) {
1306     DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1307     return Status;
1308   }
1309 
1310   mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
1311 
1312   for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1313     // Check if this NOR Flash device contain the variable storage region
1314     ContainVariableStorage =
1315         (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
1316         (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
1317 
1318     Status = NorFlashCreateInstance (
1319       NorFlashDevices[Index].DeviceBaseAddress,
1320       NorFlashDevices[Index].RegionBaseAddress,
1321       NorFlashDevices[Index].Size,
1322       Index,
1323       NorFlashDevices[Index].BlockSize,
1324       ContainVariableStorage,
1325       &NorFlashDevices[Index].Guid,
1326       &mNorFlashInstances[Index]
1327     );
1328     if (EFI_ERROR(Status)) {
1329       DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
1330     }
1331   }
1332 
1333   //
1334   // Register for the virtual address change event
1335   //
1336   Status = gBS->CreateEventEx (
1337                   EVT_NOTIFY_SIGNAL,
1338                   TPL_NOTIFY,
1339                   NorFlashVirtualNotifyEvent,
1340                   NULL,
1341                   &gEfiEventVirtualAddressChangeGuid,
1342                   &mNorFlashVirtualAddrChangeEvent
1343                   );
1344   ASSERT_EFI_ERROR (Status);
1345 
1346   return Status;
1347 }
1348