• 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 EFI_STATUS
NorFlashReadBlocks(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN BufferSizeInBytes,OUT VOID * Buffer)748 NorFlashReadBlocks (
749   IN NOR_FLASH_INSTANCE   *Instance,
750   IN EFI_LBA              Lba,
751   IN UINTN                BufferSizeInBytes,
752   OUT VOID                *Buffer
753   )
754 {
755   UINT32              NumBlocks;
756   UINTN               StartAddress;
757 
758   DEBUG((DEBUG_BLKIO, "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
759       BufferSizeInBytes, Instance->Media.BlockSize, Instance->Media.LastBlock, Lba));
760 
761   // The buffer must be valid
762   if (Buffer == NULL) {
763     return EFI_INVALID_PARAMETER;
764   }
765 
766   // Return if we have not any byte to read
767   if (BufferSizeInBytes == 0) {
768     return EFI_SUCCESS;
769   }
770 
771   // The size of the buffer must be a multiple of the block size
772   if ((BufferSizeInBytes % Instance->Media.BlockSize) != 0) {
773     return EFI_BAD_BUFFER_SIZE;
774   }
775 
776   // All blocks must be within the device
777   NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->Media.BlockSize ;
778 
779   if ((Lba + NumBlocks) > (Instance->Media.LastBlock + 1)) {
780     DEBUG((EFI_D_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
781     return EFI_INVALID_PARAMETER;
782   }
783 
784   // Get the address to start reading from
785   StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
786                                         Lba,
787                                         Instance->Media.BlockSize
788                                        );
789 
790   // Put the device into Read Array mode
791   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
792 
793   // Readout the data
794   CopyMem(Buffer, (UINTN *)StartAddress, BufferSizeInBytes);
795 
796   return EFI_SUCCESS;
797 }
798 
799 EFI_STATUS
NorFlashRead(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN Offset,IN UINTN BufferSizeInBytes,OUT VOID * Buffer)800 NorFlashRead (
801   IN NOR_FLASH_INSTANCE   *Instance,
802   IN EFI_LBA              Lba,
803   IN UINTN                Offset,
804   IN UINTN                BufferSizeInBytes,
805   OUT VOID                *Buffer
806   )
807 {
808   UINTN  StartAddress;
809 
810   // The buffer must be valid
811   if (Buffer == NULL) {
812     return EFI_INVALID_PARAMETER;
813   }
814 
815   // Return if we have not any byte to read
816   if (BufferSizeInBytes == 0) {
817     return EFI_SUCCESS;
818   }
819 
820   if (((Lba * Instance->Media.BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
821     DEBUG ((EFI_D_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
822     return EFI_INVALID_PARAMETER;
823   }
824 
825   // Get the address to start reading from
826   StartAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
827                                         Lba,
828                                         Instance->Media.BlockSize
829                                        );
830 
831   // Put the device into Read Array mode
832   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
833 
834   // Readout the data
835   CopyMem (Buffer, (UINTN *)(StartAddress + Offset), BufferSizeInBytes);
836 
837   return EFI_SUCCESS;
838 }
839 
840 /*
841   Write a full or portion of a block. It must not span block boundaries; that is,
842   Offset + *NumBytes <= Instance->Media.BlockSize.
843 */
844 EFI_STATUS
NorFlashWriteSingleBlock(IN NOR_FLASH_INSTANCE * Instance,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)845 NorFlashWriteSingleBlock (
846   IN        NOR_FLASH_INSTANCE   *Instance,
847   IN        EFI_LBA               Lba,
848   IN        UINTN                 Offset,
849   IN OUT    UINTN                *NumBytes,
850   IN        UINT8                *Buffer
851   )
852 {
853   EFI_STATUS  TempStatus;
854   UINT32      Tmp;
855   UINT32      TmpBuf;
856   UINT32      WordToWrite;
857   UINT32      Mask;
858   BOOLEAN     DoErase;
859   UINTN       BytesToWrite;
860   UINTN       CurOffset;
861   UINTN       WordAddr;
862   UINTN       BlockSize;
863   UINTN       BlockAddress;
864   UINTN       PrevBlockAddress;
865 
866   PrevBlockAddress = 0;
867 
868   if (!Instance->Initialized && Instance->Initialize) {
869     Instance->Initialize(Instance);
870   }
871 
872   DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
873 
874   // Detect WriteDisabled state
875   if (Instance->Media.ReadOnly == TRUE) {
876     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - Can not write: Device is in WriteDisabled state.\n"));
877     // It is in WriteDisabled state, return an error right away
878     return EFI_ACCESS_DENIED;
879   }
880 
881   // Cache the block size to avoid de-referencing pointers all the time
882   BlockSize = Instance->Media.BlockSize;
883 
884   // The write must not span block boundaries.
885   // We need to check each variable individually because adding two large values together overflows.
886   if ( ( Offset               >= BlockSize ) ||
887        ( *NumBytes            >  BlockSize ) ||
888        ( (Offset + *NumBytes) >  BlockSize )    ) {
889     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
890     return EFI_BAD_BUFFER_SIZE;
891   }
892 
893   // We must have some bytes to write
894   if (*NumBytes == 0) {
895     DEBUG ((EFI_D_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize ));
896     return EFI_BAD_BUFFER_SIZE;
897   }
898 
899   // Pick 128bytes as a good start for word operations as opposed to erasing the
900   // block and writing the data regardless if an erase is really needed.
901   // It looks like most individual NV variable writes are smaller than 128bytes.
902   if (*NumBytes <= 128) {
903     // Check to see if we need to erase before programming the data into NOR.
904     // If the destination bits are only changing from 1s to 0s we can just write.
905     // After a block is erased all bits in the block is set to 1.
906     // If any byte requires us to erase we just give up and rewrite all of it.
907     DoErase      = FALSE;
908     BytesToWrite = *NumBytes;
909     CurOffset    = Offset;
910 
911     while (BytesToWrite > 0) {
912       // Read full word from NOR, splice as required. A word is the smallest
913       // unit we can write.
914       TempStatus = NorFlashRead (Instance, Lba, CurOffset & ~(0x3), sizeof(Tmp), &Tmp);
915       if (EFI_ERROR (TempStatus)) {
916         return EFI_DEVICE_ERROR;
917       }
918 
919       // Physical address of word in NOR to write.
920       WordAddr = (CurOffset & ~(0x3)) + GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress,
921                                                                Lba, BlockSize);
922       // The word of data that is to be written.
923       TmpBuf = *((UINT32*)(Buffer + (*NumBytes - BytesToWrite)));
924 
925       // First do word aligned chunks.
926       if ((CurOffset & 0x3) == 0) {
927         if (BytesToWrite >= 4) {
928           // Is the destination still in 'erased' state?
929           if (~Tmp != 0) {
930             // Check to see if we are only changing bits to zero.
931             if ((Tmp ^ TmpBuf) & TmpBuf) {
932               DoErase = TRUE;
933               break;
934             }
935           }
936           // Write this word to NOR
937           WordToWrite = TmpBuf;
938           CurOffset += sizeof(TmpBuf);
939           BytesToWrite -= sizeof(TmpBuf);
940         } else {
941           // BytesToWrite < 4. Do small writes and left-overs
942           Mask = ~((~0) << (BytesToWrite * 8));
943           // Mask out the bytes we want.
944           TmpBuf &= Mask;
945           // Is the destination still in 'erased' state?
946           if ((Tmp & Mask) != Mask) {
947             // Check to see if we are only changing bits to zero.
948             if ((Tmp ^ TmpBuf) & TmpBuf) {
949               DoErase = TRUE;
950               break;
951             }
952           }
953           // Merge old and new data. Write merged word to NOR
954           WordToWrite = (Tmp & ~Mask) | TmpBuf;
955           CurOffset += BytesToWrite;
956           BytesToWrite = 0;
957         }
958       } else {
959         // Do multiple words, but starting unaligned.
960         if (BytesToWrite > (4 - (CurOffset & 0x3))) {
961           Mask = ((~0) << ((CurOffset & 0x3) * 8));
962           // Mask out the bytes we want.
963           TmpBuf &= Mask;
964           // Is the destination still in 'erased' state?
965           if ((Tmp & Mask) != Mask) {
966             // Check to see if we are only changing bits to zero.
967             if ((Tmp ^ TmpBuf) & TmpBuf) {
968               DoErase = TRUE;
969               break;
970             }
971           }
972           // Merge old and new data. Write merged word to NOR
973           WordToWrite = (Tmp & ~Mask) | TmpBuf;
974           BytesToWrite -= (4 - (CurOffset & 0x3));
975           CurOffset += (4 - (CurOffset & 0x3));
976         } else {
977           // Unaligned and fits in one word.
978           Mask = (~((~0) << (BytesToWrite * 8))) << ((CurOffset & 0x3) * 8);
979           // Mask out the bytes we want.
980           TmpBuf = (TmpBuf << ((CurOffset & 0x3) * 8)) & Mask;
981           // Is the destination still in 'erased' state?
982           if ((Tmp & Mask) != Mask) {
983             // Check to see if we are only changing bits to zero.
984             if ((Tmp ^ TmpBuf) & TmpBuf) {
985               DoErase = TRUE;
986               break;
987             }
988           }
989           // Merge old and new data. Write merged word to NOR
990           WordToWrite = (Tmp & ~Mask) | TmpBuf;
991           CurOffset += BytesToWrite;
992           BytesToWrite = 0;
993         }
994       }
995 
996       //
997       // Write the word to NOR.
998       //
999 
1000       BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
1001       if (BlockAddress != PrevBlockAddress) {
1002         TempStatus = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
1003         if (EFI_ERROR (TempStatus)) {
1004           return EFI_DEVICE_ERROR;
1005         }
1006         PrevBlockAddress = BlockAddress;
1007       }
1008       TempStatus = NorFlashWriteSingleWord (Instance, WordAddr, WordToWrite);
1009       if (EFI_ERROR (TempStatus)) {
1010         return EFI_DEVICE_ERROR;
1011       }
1012     }
1013     // Exit if we got here and could write all the data. Otherwise do the
1014     // Erase-Write cycle.
1015     if (!DoErase) {
1016       return EFI_SUCCESS;
1017     }
1018   }
1019 
1020   // Check we did get some memory. Buffer is BlockSize.
1021   if (Instance->ShadowBuffer == NULL) {
1022     DEBUG ((EFI_D_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
1023     return EFI_DEVICE_ERROR;
1024   }
1025 
1026   // Read NOR Flash data into shadow buffer
1027   TempStatus = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
1028   if (EFI_ERROR (TempStatus)) {
1029     // Return one of the pre-approved error statuses
1030     return EFI_DEVICE_ERROR;
1031   }
1032 
1033   // Put the data at the appropriate location inside the buffer area
1034   CopyMem ((VOID*)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
1035 
1036   // Write the modified buffer back to the NorFlash
1037   TempStatus = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
1038   if (EFI_ERROR (TempStatus)) {
1039     // Return one of the pre-approved error statuses
1040     return EFI_DEVICE_ERROR;
1041   }
1042 
1043   return EFI_SUCCESS;
1044 }
1045 
1046 /*
1047   Although DiskIoDxe will automatically install the DiskIO protocol whenever
1048   we install the BlockIO protocol, its implementation is sub-optimal as it reads
1049   and writes entire blocks using the BlockIO protocol. In fact we can access
1050   NOR flash with a finer granularity than that, so we can improve performance
1051   by directly producing the DiskIO protocol.
1052 */
1053 
1054 /**
1055   Read BufferSize bytes from Offset into Buffer.
1056 
1057   @param  This                  Protocol instance pointer.
1058   @param  MediaId               Id of the media, changes every time the media is replaced.
1059   @param  Offset                The starting byte offset to read from
1060   @param  BufferSize            Size of Buffer
1061   @param  Buffer                Buffer containing read data
1062 
1063   @retval EFI_SUCCESS           The data was read correctly from the device.
1064   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
1065   @retval EFI_NO_MEDIA          There is no media in the device.
1066   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
1067   @retval EFI_INVALID_PARAMETER The read request contains device addresses that are not
1068                                 valid for the device.
1069 
1070 **/
1071 EFI_STATUS
1072 EFIAPI
NorFlashDiskIoReadDisk(IN EFI_DISK_IO_PROTOCOL * This,IN UINT32 MediaId,IN UINT64 DiskOffset,IN UINTN BufferSize,OUT VOID * Buffer)1073 NorFlashDiskIoReadDisk (
1074   IN EFI_DISK_IO_PROTOCOL         *This,
1075   IN UINT32                       MediaId,
1076   IN UINT64                       DiskOffset,
1077   IN UINTN                        BufferSize,
1078   OUT VOID                        *Buffer
1079   )
1080 {
1081   NOR_FLASH_INSTANCE *Instance;
1082   UINT32              BlockSize;
1083   UINT32              BlockOffset;
1084   EFI_LBA             Lba;
1085 
1086   Instance = INSTANCE_FROM_DISKIO_THIS(This);
1087 
1088   if (MediaId != Instance->Media.MediaId) {
1089     return EFI_MEDIA_CHANGED;
1090   }
1091 
1092   BlockSize = Instance->Media.BlockSize;
1093   Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1094 
1095   return NorFlashRead (Instance, Lba, BlockOffset, BufferSize, Buffer);
1096 }
1097 
1098 /**
1099   Writes a specified number of bytes to a device.
1100 
1101   @param  This       Indicates a pointer to the calling context.
1102   @param  MediaId    ID of the medium to be written.
1103   @param  Offset     The starting byte offset on the logical block I/O device to write.
1104   @param  BufferSize The size in bytes of Buffer. The number of bytes to write to the device.
1105   @param  Buffer     A pointer to the buffer containing the data to be written.
1106 
1107   @retval EFI_SUCCESS           The data was written correctly to the device.
1108   @retval EFI_WRITE_PROTECTED   The device can not be written to.
1109   @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
1110   @retval EFI_NO_MEDIA          There is no media in the device.
1111   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
1112   @retval EFI_INVALID_PARAMETER The write request contains device addresses that are not
1113                                  valid for the device.
1114 
1115 **/
1116 EFI_STATUS
1117 EFIAPI
NorFlashDiskIoWriteDisk(IN EFI_DISK_IO_PROTOCOL * This,IN UINT32 MediaId,IN UINT64 DiskOffset,IN UINTN BufferSize,IN VOID * Buffer)1118 NorFlashDiskIoWriteDisk (
1119   IN EFI_DISK_IO_PROTOCOL         *This,
1120   IN UINT32                       MediaId,
1121   IN UINT64                       DiskOffset,
1122   IN UINTN                        BufferSize,
1123   IN VOID                         *Buffer
1124   )
1125 {
1126   NOR_FLASH_INSTANCE *Instance;
1127   UINT32              BlockSize;
1128   UINT32              BlockOffset;
1129   EFI_LBA             Lba;
1130   UINTN               RemainingBytes;
1131   UINTN               WriteSize;
1132   EFI_STATUS          Status;
1133 
1134   Instance = INSTANCE_FROM_DISKIO_THIS(This);
1135 
1136   if (MediaId != Instance->Media.MediaId) {
1137     return EFI_MEDIA_CHANGED;
1138   }
1139 
1140   BlockSize = Instance->Media.BlockSize;
1141   Lba = (EFI_LBA) DivU64x32Remainder (DiskOffset, BlockSize, &BlockOffset);
1142 
1143   RemainingBytes = BufferSize;
1144 
1145   // Write either all the remaining bytes, or the number of bytes that bring
1146   // us up to a block boundary, whichever is less.
1147   // (DiskOffset | (BlockSize - 1)) + 1) rounds DiskOffset up to the next
1148   // block boundary (even if it is already on one).
1149   WriteSize = MIN (RemainingBytes, ((DiskOffset | (BlockSize - 1)) + 1) - DiskOffset);
1150 
1151   do {
1152     if (WriteSize == BlockSize) {
1153       // Write a full block
1154       Status = NorFlashWriteFullBlock (Instance, Lba, Buffer, BlockSize / sizeof (UINT32));
1155     } else {
1156       // Write a partial block
1157       Status = NorFlashWriteSingleBlock (Instance, Lba, BlockOffset, &WriteSize, Buffer);
1158     }
1159     if (EFI_ERROR (Status)) {
1160       return Status;
1161     }
1162     // Now continue writing either all the remaining bytes or single blocks.
1163     RemainingBytes -= WriteSize;
1164     Buffer = (UINT8 *) Buffer + WriteSize;
1165     Lba++;
1166     BlockOffset = 0;
1167     WriteSize = MIN (RemainingBytes, BlockSize);
1168   } while (RemainingBytes);
1169 
1170   return Status;
1171 }
1172 
1173 EFI_STATUS
NorFlashReset(IN NOR_FLASH_INSTANCE * Instance)1174 NorFlashReset (
1175   IN  NOR_FLASH_INSTANCE *Instance
1176   )
1177 {
1178   // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
1179   SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
1180   return EFI_SUCCESS;
1181 }
1182 
1183 /**
1184   Fixup internal data so that EFI can be call in virtual mode.
1185   Call the passed in Child Notify event and convert any pointers in
1186   lib to virtual mode.
1187 
1188   @param[in]    Event   The Event that is being processed
1189   @param[in]    Context Event Context
1190 **/
1191 VOID
1192 EFIAPI
NorFlashVirtualNotifyEvent(IN EFI_EVENT Event,IN VOID * Context)1193 NorFlashVirtualNotifyEvent (
1194   IN EFI_EVENT        Event,
1195   IN VOID             *Context
1196   )
1197 {
1198   UINTN Index;
1199 
1200   for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1201     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->DeviceBaseAddress);
1202     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->RegionBaseAddress);
1203 
1204     // Convert BlockIo protocol
1205     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.FlushBlocks);
1206     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.ReadBlocks);
1207     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.Reset);
1208     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->BlockIoProtocol.WriteBlocks);
1209 
1210     // Convert Fvb
1211     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
1212     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
1213     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
1214     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
1215     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Read);
1216     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
1217     EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->FvbProtocol.Write);
1218 
1219     if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
1220       EfiConvertPointer (0x0, (VOID**)&mNorFlashInstances[Index]->ShadowBuffer);
1221     }
1222   }
1223 
1224   return;
1225 }
1226 
1227 EFI_STATUS
1228 EFIAPI
NorFlashInitialise(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)1229 NorFlashInitialise (
1230   IN EFI_HANDLE         ImageHandle,
1231   IN EFI_SYSTEM_TABLE   *SystemTable
1232   )
1233 {
1234   EFI_STATUS              Status;
1235   UINT32                  Index;
1236   NOR_FLASH_DESCRIPTION*  NorFlashDevices;
1237   BOOLEAN                 ContainVariableStorage;
1238 
1239   Status = NorFlashPlatformInitialization ();
1240   if (EFI_ERROR(Status)) {
1241     DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to initialize Nor Flash devices\n"));
1242     return Status;
1243   }
1244 
1245   Status = NorFlashPlatformGetDevices (&NorFlashDevices, &mNorFlashDeviceCount);
1246   if (EFI_ERROR(Status)) {
1247     DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to get Nor Flash devices\n"));
1248     return Status;
1249   }
1250 
1251   mNorFlashInstances = AllocateRuntimePool (sizeof(NOR_FLASH_INSTANCE*) * mNorFlashDeviceCount);
1252 
1253   for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
1254     // Check if this NOR Flash device contain the variable storage region
1255     ContainVariableStorage =
1256         (NorFlashDevices[Index].RegionBaseAddress <= PcdGet32 (PcdFlashNvStorageVariableBase)) &&
1257         (PcdGet32 (PcdFlashNvStorageVariableBase) + PcdGet32 (PcdFlashNvStorageVariableSize) <= NorFlashDevices[Index].RegionBaseAddress + NorFlashDevices[Index].Size);
1258 
1259     Status = NorFlashCreateInstance (
1260       NorFlashDevices[Index].DeviceBaseAddress,
1261       NorFlashDevices[Index].RegionBaseAddress,
1262       NorFlashDevices[Index].Size,
1263       Index,
1264       NorFlashDevices[Index].BlockSize,
1265       ContainVariableStorage,
1266       &NorFlashDevices[Index].Guid,
1267       &mNorFlashInstances[Index]
1268     );
1269     if (EFI_ERROR(Status)) {
1270       DEBUG((EFI_D_ERROR,"NorFlashInitialise: Fail to create instance for NorFlash[%d]\n",Index));
1271     }
1272   }
1273 
1274   //
1275   // Register for the virtual address change event
1276   //
1277   Status = gBS->CreateEventEx (
1278                   EVT_NOTIFY_SIGNAL,
1279                   TPL_NOTIFY,
1280                   NorFlashVirtualNotifyEvent,
1281                   NULL,
1282                   &gEfiEventVirtualAddressChangeGuid,
1283                   &mNorFlashVirtualAddrChangeEvent
1284                   );
1285   ASSERT_EFI_ERROR (Status);
1286 
1287   return Status;
1288 }
1289