]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/VirtNorFlashDxe/VirtNorFlash.c
OvmfPkg/VirtNorFlashDxe: avoid switching between modes in a tight loop
[mirror_edk2.git] / OvmfPkg / VirtNorFlashDxe / VirtNorFlash.c
1 /** @file NorFlash.c
2
3 Copyright (c) 2011 - 2020, Arm Limited. All rights reserved.<BR>
4 Copyright (c) 2020, Linaro, Ltd. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Library/BaseMemoryLib.h>
11
12 #include "VirtNorFlash.h"
13
14 //
15 // Global variable declarations
16 //
17 extern NOR_FLASH_INSTANCE **mNorFlashInstances;
18 extern UINT32 mNorFlashDeviceCount;
19
20 UINT32
21 NorFlashReadStatusRegister (
22 IN NOR_FLASH_INSTANCE *Instance,
23 IN UINTN SR_Address
24 )
25 {
26 // Prepare to read the status register
27 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_STATUS_REGISTER);
28 return MmioRead32 (Instance->DeviceBaseAddress);
29 }
30
31 STATIC
32 BOOLEAN
33 NorFlashBlockIsLocked (
34 IN NOR_FLASH_INSTANCE *Instance,
35 IN UINTN BlockAddress
36 )
37 {
38 UINT32 LockStatus;
39
40 // Send command for reading device id
41 SEND_NOR_COMMAND (BlockAddress, 2, P30_CMD_READ_DEVICE_ID);
42
43 // Read block lock status
44 LockStatus = MmioRead32 (CREATE_NOR_ADDRESS (BlockAddress, 2));
45
46 // Decode block lock status
47 LockStatus = FOLD_32BIT_INTO_16BIT (LockStatus);
48
49 if ((LockStatus & 0x2) != 0) {
50 DEBUG ((DEBUG_ERROR, "NorFlashBlockIsLocked: WARNING: Block LOCKED DOWN\n"));
51 }
52
53 return ((LockStatus & 0x1) != 0);
54 }
55
56 STATIC
57 EFI_STATUS
58 NorFlashUnlockSingleBlock (
59 IN NOR_FLASH_INSTANCE *Instance,
60 IN UINTN BlockAddress
61 )
62 {
63 UINT32 LockStatus;
64
65 // Raise the Task Priority Level to TPL_NOTIFY to serialise all its operations
66 // and to protect shared data structures.
67
68 // Request a lock setup
69 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_LOCK_BLOCK_SETUP);
70
71 // Request an unlock
72 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_UNLOCK_BLOCK);
73
74 // Wait until the status register gives us the all clear
75 do {
76 LockStatus = NorFlashReadStatusRegister (Instance, BlockAddress);
77 } while ((LockStatus & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
78
79 // Put device back into Read Array mode
80 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_READ_ARRAY);
81
82 DEBUG ((DEBUG_BLKIO, "UnlockSingleBlock: BlockAddress=0x%08x\n", BlockAddress));
83
84 return EFI_SUCCESS;
85 }
86
87 EFI_STATUS
88 NorFlashUnlockSingleBlockIfNecessary (
89 IN NOR_FLASH_INSTANCE *Instance,
90 IN UINTN BlockAddress
91 )
92 {
93 EFI_STATUS Status;
94
95 Status = EFI_SUCCESS;
96
97 if (NorFlashBlockIsLocked (Instance, BlockAddress)) {
98 Status = NorFlashUnlockSingleBlock (Instance, BlockAddress);
99 }
100
101 return Status;
102 }
103
104 /**
105 * The following function presumes that the block has already been unlocked.
106 **/
107 EFI_STATUS
108 NorFlashEraseSingleBlock (
109 IN NOR_FLASH_INSTANCE *Instance,
110 IN UINTN BlockAddress
111 )
112 {
113 EFI_STATUS Status;
114 UINT32 StatusRegister;
115
116 Status = EFI_SUCCESS;
117
118 // Request a block erase and then confirm it
119 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_SETUP);
120 SEND_NOR_COMMAND (BlockAddress, 0, P30_CMD_BLOCK_ERASE_CONFIRM);
121
122 // Wait until the status register gives us the all clear
123 do {
124 StatusRegister = NorFlashReadStatusRegister (Instance, BlockAddress);
125 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
126
127 if (StatusRegister & P30_SR_BIT_VPP) {
128 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: VPP Range Error\n", BlockAddress));
129 Status = EFI_DEVICE_ERROR;
130 }
131
132 if ((StatusRegister & (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) == (P30_SR_BIT_ERASE | P30_SR_BIT_PROGRAM)) {
133 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Command Sequence Error\n", BlockAddress));
134 Status = EFI_DEVICE_ERROR;
135 }
136
137 if (StatusRegister & P30_SR_BIT_ERASE) {
138 DEBUG ((DEBUG_ERROR, "EraseSingleBlock(BlockAddress=0x%08x: Block Erase Error StatusRegister:0x%X\n", BlockAddress, StatusRegister));
139 Status = EFI_DEVICE_ERROR;
140 }
141
142 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
143 // The debug level message has been reduced because a device lock might happen. In this case we just retry it ...
144 DEBUG ((DEBUG_INFO, "EraseSingleBlock(BlockAddress=0x%08x: Block Locked Error\n", BlockAddress));
145 Status = EFI_WRITE_PROTECTED;
146 }
147
148 if (EFI_ERROR (Status)) {
149 // Clear the Status Register
150 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
151 }
152
153 // Put device back into Read Array mode
154 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
155
156 return Status;
157 }
158
159 EFI_STATUS
160 NorFlashWriteSingleWord (
161 IN NOR_FLASH_INSTANCE *Instance,
162 IN UINTN WordAddress,
163 IN UINT32 WriteData
164 )
165 {
166 EFI_STATUS Status;
167 UINT32 StatusRegister;
168
169 Status = EFI_SUCCESS;
170
171 // Request a write single word command
172 SEND_NOR_COMMAND (WordAddress, 0, P30_CMD_WORD_PROGRAM_SETUP);
173
174 // Store the word into NOR Flash;
175 MmioWrite32 (WordAddress, WriteData);
176
177 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
178 do {
179 // Prepare to read the status register
180 StatusRegister = NorFlashReadStatusRegister (Instance, WordAddress);
181 // The chip is busy while the WRITE bit is not asserted
182 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
183
184 // Perform a full status check:
185 // Mask the relevant bits of Status Register.
186 // Everything should be zero, if not, we have a problem
187
188 if (StatusRegister & P30_SR_BIT_VPP) {
189 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): VPP Range Error\n", WordAddress));
190 Status = EFI_DEVICE_ERROR;
191 }
192
193 if (StatusRegister & P30_SR_BIT_PROGRAM) {
194 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Program Error\n", WordAddress));
195 Status = EFI_DEVICE_ERROR;
196 }
197
198 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
199 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleWord(WordAddress:0x%X): Device Protect Error\n", WordAddress));
200 Status = EFI_DEVICE_ERROR;
201 }
202
203 if (!EFI_ERROR (Status)) {
204 // Clear the Status Register
205 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
206 }
207
208 return Status;
209 }
210
211 /*
212 * Writes data to the NOR Flash using the Buffered Programming method.
213 *
214 * The maximum size of the on-chip buffer is 32-words, because of hardware restrictions.
215 * Therefore this function will only handle buffers up to 32 words or 128 bytes.
216 * To deal with larger buffers, call this function again.
217 *
218 * This function presumes that both the TargetAddress and the TargetAddress+BufferSize
219 * exist entirely within the NOR Flash. Therefore these conditions will not be checked here.
220 *
221 * In buffered programming, if the target address not at the beginning of a 32-bit word boundary,
222 * then programming time is doubled and power consumption is increased.
223 * Therefore, it is a requirement to align buffer writes to 32-bit word boundaries.
224 * i.e. the last 4 bits of the target start address must be zero: 0x......00
225 */
226 EFI_STATUS
227 NorFlashWriteBuffer (
228 IN NOR_FLASH_INSTANCE *Instance,
229 IN UINTN TargetAddress,
230 IN UINTN BufferSizeInBytes,
231 IN UINT32 *Buffer
232 )
233 {
234 EFI_STATUS Status;
235 UINTN BufferSizeInWords;
236 UINTN Count;
237 volatile UINT32 *Data;
238 UINTN WaitForBuffer;
239 BOOLEAN BufferAvailable;
240 UINT32 StatusRegister;
241
242 WaitForBuffer = MAX_BUFFERED_PROG_ITERATIONS;
243 BufferAvailable = FALSE;
244
245 // Check that the target address does not cross a 32-word boundary.
246 if ((TargetAddress & BOUNDARY_OF_32_WORDS) != 0) {
247 return EFI_INVALID_PARAMETER;
248 }
249
250 // Check there are some data to program
251 if (BufferSizeInBytes == 0) {
252 return EFI_BUFFER_TOO_SMALL;
253 }
254
255 // Check that the buffer size does not exceed the maximum hardware buffer size on chip.
256 if (BufferSizeInBytes > P30_MAX_BUFFER_SIZE_IN_BYTES) {
257 return EFI_BAD_BUFFER_SIZE;
258 }
259
260 // Check that the buffer size is a multiple of 32-bit words
261 if ((BufferSizeInBytes % 4) != 0) {
262 return EFI_BAD_BUFFER_SIZE;
263 }
264
265 // Pre-programming conditions checked, now start the algorithm.
266
267 // Prepare the data destination address
268 Data = (UINT32 *)TargetAddress;
269
270 // Check the availability of the buffer
271 do {
272 // Issue the Buffered Program Setup command
273 SEND_NOR_COMMAND (TargetAddress, 0, P30_CMD_BUFFERED_PROGRAM_SETUP);
274
275 // Read back the status register bit#7 from the same address
276 if (((*Data) & P30_SR_BIT_WRITE) == P30_SR_BIT_WRITE) {
277 BufferAvailable = TRUE;
278 }
279
280 // Update the loop counter
281 WaitForBuffer--;
282 } while ((WaitForBuffer > 0) && (BufferAvailable == FALSE));
283
284 // The buffer was not available for writing
285 if (WaitForBuffer == 0) {
286 return EFI_DEVICE_ERROR;
287 }
288
289 // From now on we work in 32-bit words
290 BufferSizeInWords = BufferSizeInBytes / (UINTN)4;
291
292 // Write the word count, which is (buffer_size_in_words - 1),
293 // because word count 0 means one word.
294 SEND_NOR_COMMAND (TargetAddress, 0, (BufferSizeInWords - 1));
295
296 // Write the data to the NOR Flash, advancing each address by 4 bytes
297 for (Count = 0; Count < BufferSizeInWords; Count++, Data++, Buffer++) {
298 MmioWrite32 ((UINTN)Data, *Buffer);
299 }
300
301 // Issue the Buffered Program Confirm command, to start the programming operation
302 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_BUFFERED_PROGRAM_CONFIRM);
303
304 // Wait for the write to complete and then check for any errors; i.e. check the Status Register
305 do {
306 StatusRegister = NorFlashReadStatusRegister (Instance, TargetAddress);
307 // The chip is busy while the WRITE bit is not asserted
308 } while ((StatusRegister & P30_SR_BIT_WRITE) != P30_SR_BIT_WRITE);
309
310 // Perform a full status check:
311 // Mask the relevant bits of Status Register.
312 // Everything should be zero, if not, we have a problem
313
314 Status = EFI_SUCCESS;
315
316 if (StatusRegister & P30_SR_BIT_VPP) {
317 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): VPP Range Error\n", TargetAddress));
318 Status = EFI_DEVICE_ERROR;
319 }
320
321 if (StatusRegister & P30_SR_BIT_PROGRAM) {
322 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Program Error\n", TargetAddress));
323 Status = EFI_DEVICE_ERROR;
324 }
325
326 if (StatusRegister & P30_SR_BIT_BLOCK_LOCKED) {
327 DEBUG ((DEBUG_ERROR, "NorFlashWriteBuffer(TargetAddress:0x%X): Device Protect Error\n", TargetAddress));
328 Status = EFI_DEVICE_ERROR;
329 }
330
331 if (!EFI_ERROR (Status)) {
332 // Clear the Status Register
333 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_CLEAR_STATUS_REGISTER);
334 }
335
336 return Status;
337 }
338
339 EFI_STATUS
340 NorFlashWriteBlocks (
341 IN NOR_FLASH_INSTANCE *Instance,
342 IN EFI_LBA Lba,
343 IN UINTN BufferSizeInBytes,
344 IN VOID *Buffer
345 )
346 {
347 UINT32 *pWriteBuffer;
348 EFI_STATUS Status;
349 EFI_LBA CurrentBlock;
350 UINT32 BlockSizeInWords;
351 UINT32 NumBlocks;
352 UINT32 BlockCount;
353
354 Status = EFI_SUCCESS;
355
356 // The buffer must be valid
357 if (Buffer == NULL) {
358 return EFI_INVALID_PARAMETER;
359 }
360
361 // We must have some bytes to read
362 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BufferSizeInBytes=0x%x\n", BufferSizeInBytes));
363 if (BufferSizeInBytes == 0) {
364 return EFI_BAD_BUFFER_SIZE;
365 }
366
367 // The size of the buffer must be a multiple of the block size
368 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: BlockSize in bytes =0x%x\n", Instance->BlockSize));
369 if ((BufferSizeInBytes % Instance->BlockSize) != 0) {
370 return EFI_BAD_BUFFER_SIZE;
371 }
372
373 // All blocks must be within the device
374 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
375
376 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: NumBlocks=%d, LastBlock=%ld, Lba=%ld.\n", NumBlocks, Instance->LastBlock, Lba));
377
378 if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) {
379 DEBUG ((DEBUG_ERROR, "NorFlashWriteBlocks: ERROR - Write will exceed last block.\n"));
380 return EFI_INVALID_PARAMETER;
381 }
382
383 BlockSizeInWords = Instance->BlockSize / 4;
384
385 // Because the target *Buffer is a pointer to VOID, we must put all the data into a pointer
386 // to a proper data type, so use *ReadBuffer
387 pWriteBuffer = (UINT32 *)Buffer;
388
389 CurrentBlock = Lba;
390 for (BlockCount = 0; BlockCount < NumBlocks; BlockCount++, CurrentBlock++, pWriteBuffer = pWriteBuffer + BlockSizeInWords) {
391 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Writing block #%d\n", (UINTN)CurrentBlock));
392
393 Status = NorFlashWriteFullBlock (Instance, CurrentBlock, pWriteBuffer, BlockSizeInWords);
394
395 if (EFI_ERROR (Status)) {
396 break;
397 }
398 }
399
400 DEBUG ((DEBUG_BLKIO, "NorFlashWriteBlocks: Exit Status = \"%r\".\n", Status));
401 return Status;
402 }
403
404 #define BOTH_ALIGNED(a, b, align) ((((UINTN)(a) | (UINTN)(b)) & ((align) - 1)) == 0)
405
406 /**
407 Copy Length bytes from Source to Destination, using aligned accesses only.
408 Note that this implementation uses memcpy() semantics rather then memmove()
409 semantics, i.e., SourceBuffer and DestinationBuffer should not overlap.
410
411 @param DestinationBuffer The target of the copy request.
412 @param SourceBuffer The place to copy from.
413 @param Length The number of bytes to copy.
414
415 @return Destination
416
417 **/
418 STATIC
419 VOID *
420 AlignedCopyMem (
421 OUT VOID *DestinationBuffer,
422 IN CONST VOID *SourceBuffer,
423 IN UINTN Length
424 )
425 {
426 UINT8 *Destination8;
427 CONST UINT8 *Source8;
428 UINT32 *Destination32;
429 CONST UINT32 *Source32;
430 UINT64 *Destination64;
431 CONST UINT64 *Source64;
432
433 if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 8) && (Length >= 8)) {
434 Destination64 = DestinationBuffer;
435 Source64 = SourceBuffer;
436 while (Length >= 8) {
437 *Destination64++ = *Source64++;
438 Length -= 8;
439 }
440
441 Destination8 = (UINT8 *)Destination64;
442 Source8 = (CONST UINT8 *)Source64;
443 } else if (BOTH_ALIGNED (DestinationBuffer, SourceBuffer, 4) && (Length >= 4)) {
444 Destination32 = DestinationBuffer;
445 Source32 = SourceBuffer;
446 while (Length >= 4) {
447 *Destination32++ = *Source32++;
448 Length -= 4;
449 }
450
451 Destination8 = (UINT8 *)Destination32;
452 Source8 = (CONST UINT8 *)Source32;
453 } else {
454 Destination8 = DestinationBuffer;
455 Source8 = SourceBuffer;
456 }
457
458 while (Length-- != 0) {
459 *Destination8++ = *Source8++;
460 }
461
462 return DestinationBuffer;
463 }
464
465 EFI_STATUS
466 NorFlashReadBlocks (
467 IN NOR_FLASH_INSTANCE *Instance,
468 IN EFI_LBA Lba,
469 IN UINTN BufferSizeInBytes,
470 OUT VOID *Buffer
471 )
472 {
473 UINT32 NumBlocks;
474 UINTN StartAddress;
475
476 DEBUG ((
477 DEBUG_BLKIO,
478 "NorFlashReadBlocks: BufferSize=0x%xB BlockSize=0x%xB LastBlock=%ld, Lba=%ld.\n",
479 BufferSizeInBytes,
480 Instance->BlockSize,
481 Instance->LastBlock,
482 Lba
483 ));
484
485 // The buffer must be valid
486 if (Buffer == NULL) {
487 return EFI_INVALID_PARAMETER;
488 }
489
490 // Return if we have not any byte to read
491 if (BufferSizeInBytes == 0) {
492 return EFI_SUCCESS;
493 }
494
495 // The size of the buffer must be a multiple of the block size
496 if ((BufferSizeInBytes % Instance->BlockSize) != 0) {
497 return EFI_BAD_BUFFER_SIZE;
498 }
499
500 // All blocks must be within the device
501 NumBlocks = ((UINT32)BufferSizeInBytes) / Instance->BlockSize;
502
503 if ((Lba + NumBlocks) > (Instance->LastBlock + 1)) {
504 DEBUG ((DEBUG_ERROR, "NorFlashReadBlocks: ERROR - Read will exceed last block\n"));
505 return EFI_INVALID_PARAMETER;
506 }
507
508 // Get the address to start reading from
509 StartAddress = GET_NOR_BLOCK_ADDRESS (
510 Instance->RegionBaseAddress,
511 Lba,
512 Instance->BlockSize
513 );
514
515 // Put the device into Read Array mode
516 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
517
518 // Readout the data
519 AlignedCopyMem (Buffer, (VOID *)StartAddress, BufferSizeInBytes);
520
521 return EFI_SUCCESS;
522 }
523
524 EFI_STATUS
525 NorFlashRead (
526 IN NOR_FLASH_INSTANCE *Instance,
527 IN EFI_LBA Lba,
528 IN UINTN Offset,
529 IN UINTN BufferSizeInBytes,
530 OUT VOID *Buffer
531 )
532 {
533 UINTN StartAddress;
534
535 // The buffer must be valid
536 if (Buffer == NULL) {
537 return EFI_INVALID_PARAMETER;
538 }
539
540 // Return if we have not any byte to read
541 if (BufferSizeInBytes == 0) {
542 return EFI_SUCCESS;
543 }
544
545 if (((Lba * Instance->BlockSize) + Offset + BufferSizeInBytes) > Instance->Size) {
546 DEBUG ((DEBUG_ERROR, "NorFlashRead: ERROR - Read will exceed device size.\n"));
547 return EFI_INVALID_PARAMETER;
548 }
549
550 // Get the address to start reading from
551 StartAddress = GET_NOR_BLOCK_ADDRESS (
552 Instance->RegionBaseAddress,
553 Lba,
554 Instance->BlockSize
555 );
556
557 // Put the device into Read Array mode
558 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
559
560 // Readout the data
561 AlignedCopyMem (Buffer, (VOID *)(StartAddress + Offset), BufferSizeInBytes);
562
563 return EFI_SUCCESS;
564 }
565
566 /*
567 Write a full or portion of a block. It must not span block boundaries; that is,
568 Offset + *NumBytes <= Instance->BlockSize.
569 */
570 EFI_STATUS
571 NorFlashWriteSingleBlock (
572 IN NOR_FLASH_INSTANCE *Instance,
573 IN EFI_LBA Lba,
574 IN UINTN Offset,
575 IN OUT UINTN *NumBytes,
576 IN UINT8 *Buffer
577 )
578 {
579 EFI_STATUS Status;
580 UINTN CurOffset;
581 UINTN BlockSize;
582 UINTN BlockAddress;
583 UINT8 *OrigData;
584
585 DEBUG ((DEBUG_BLKIO, "NorFlashWriteSingleBlock(Parameters: Lba=%ld, Offset=0x%x, *NumBytes=0x%x, Buffer @ 0x%08x)\n", Lba, Offset, *NumBytes, Buffer));
586
587 // Check we did get some memory. Buffer is BlockSize.
588 if (Instance->ShadowBuffer == NULL) {
589 DEBUG ((DEBUG_ERROR, "FvbWrite: ERROR - Buffer not ready\n"));
590 return EFI_DEVICE_ERROR;
591 }
592
593 // Cache the block size to avoid de-referencing pointers all the time
594 BlockSize = Instance->BlockSize;
595
596 // The write must not span block boundaries.
597 // We need to check each variable individually because adding two large values together overflows.
598 if ((Offset >= BlockSize) ||
599 (*NumBytes > BlockSize) ||
600 ((Offset + *NumBytes) > BlockSize))
601 {
602 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
603 return EFI_BAD_BUFFER_SIZE;
604 }
605
606 // We must have some bytes to write
607 if (*NumBytes == 0) {
608 DEBUG ((DEBUG_ERROR, "NorFlashWriteSingleBlock: ERROR - EFI_BAD_BUFFER_SIZE: (Offset=0x%x + NumBytes=0x%x) > BlockSize=0x%x\n", Offset, *NumBytes, BlockSize));
609 return EFI_BAD_BUFFER_SIZE;
610 }
611
612 // Pick P30_MAX_BUFFER_SIZE_IN_BYTES (== 128 bytes) as a good start for word
613 // operations as opposed to erasing the block and writing the data regardless
614 // if an erase is really needed. It looks like most individual NV variable
615 // writes are smaller than 128 bytes.
616 // To avoid pathological cases were a 2 byte write is disregarded because it
617 // occurs right at a 128 byte buffered write alignment boundary, permit up to
618 // twice the max buffer size, and perform two writes if needed.
619 if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) <= (2 * P30_MAX_BUFFER_SIZE_IN_BYTES)) {
620 // Check to see if we need to erase before programming the data into NOR.
621 // If the destination bits are only changing from 1s to 0s we can just write.
622 // After a block is erased all bits in the block is set to 1.
623 // If any byte requires us to erase we just give up and rewrite all of it.
624
625 // Read the old version of the data into the shadow buffer
626 Status = NorFlashRead (
627 Instance,
628 Lba,
629 Offset & ~BOUNDARY_OF_32_WORDS,
630 (*NumBytes | BOUNDARY_OF_32_WORDS) + 1,
631 Instance->ShadowBuffer
632 );
633 if (EFI_ERROR (Status)) {
634 return EFI_DEVICE_ERROR;
635 }
636
637 // Make OrigData point to the start of the old version of the data inside
638 // the word aligned buffer
639 OrigData = Instance->ShadowBuffer + (Offset & BOUNDARY_OF_32_WORDS);
640
641 // Update the buffer containing the old version of the data with the new
642 // contents, while checking whether the old version had any bits cleared
643 // that we want to set. In that case, we will need to erase the block first.
644 for (CurOffset = 0; CurOffset < *NumBytes; CurOffset++) {
645 if (~OrigData[CurOffset] & Buffer[CurOffset]) {
646 goto DoErase;
647 }
648
649 OrigData[CurOffset] = Buffer[CurOffset];
650 }
651
652 //
653 // Write the updated buffer to NOR.
654 //
655 BlockAddress = GET_NOR_BLOCK_ADDRESS (Instance->RegionBaseAddress, Lba, BlockSize);
656
657 // Unlock the block if we have to
658 Status = NorFlashUnlockSingleBlockIfNecessary (Instance, BlockAddress);
659 if (EFI_ERROR (Status)) {
660 goto Exit;
661 }
662
663 Status = NorFlashWriteBuffer (
664 Instance,
665 BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
666 P30_MAX_BUFFER_SIZE_IN_BYTES,
667 Instance->ShadowBuffer
668 );
669 if (EFI_ERROR (Status)) {
670 goto Exit;
671 }
672
673 if ((*NumBytes + (Offset & BOUNDARY_OF_32_WORDS)) > P30_MAX_BUFFER_SIZE_IN_BYTES) {
674 BlockAddress += P30_MAX_BUFFER_SIZE_IN_BYTES;
675
676 Status = NorFlashWriteBuffer (
677 Instance,
678 BlockAddress + (Offset & ~BOUNDARY_OF_32_WORDS),
679 P30_MAX_BUFFER_SIZE_IN_BYTES,
680 Instance->ShadowBuffer + P30_MAX_BUFFER_SIZE_IN_BYTES
681 );
682 }
683
684 Exit:
685 // Put device back into Read Array mode
686 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
687
688 return Status;
689 }
690
691 DoErase:
692 // Read NOR Flash data into shadow buffer
693 Status = NorFlashReadBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
694 if (EFI_ERROR (Status)) {
695 // Return one of the pre-approved error statuses
696 return EFI_DEVICE_ERROR;
697 }
698
699 // Put the data at the appropriate location inside the buffer area
700 CopyMem ((VOID *)((UINTN)Instance->ShadowBuffer + Offset), Buffer, *NumBytes);
701
702 // Write the modified buffer back to the NorFlash
703 Status = NorFlashWriteBlocks (Instance, Lba, BlockSize, Instance->ShadowBuffer);
704 if (EFI_ERROR (Status)) {
705 // Return one of the pre-approved error statuses
706 return EFI_DEVICE_ERROR;
707 }
708
709 return EFI_SUCCESS;
710 }
711
712 EFI_STATUS
713 NorFlashReset (
714 IN NOR_FLASH_INSTANCE *Instance
715 )
716 {
717 // As there is no specific RESET to perform, ensure that the devices is in the default Read Array mode
718 SEND_NOR_COMMAND (Instance->DeviceBaseAddress, 0, P30_CMD_READ_ARRAY);
719 return EFI_SUCCESS;
720 }
721
722 /**
723 Fixup internal data so that EFI can be call in virtual mode.
724 Call the passed in Child Notify event and convert any pointers in
725 lib to virtual mode.
726
727 @param[in] Event The Event that is being processed
728 @param[in] Context Event Context
729 **/
730 VOID
731 EFIAPI
732 NorFlashVirtualNotifyEvent (
733 IN EFI_EVENT Event,
734 IN VOID *Context
735 )
736 {
737 UINTN Index;
738
739 for (Index = 0; Index < mNorFlashDeviceCount; Index++) {
740 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->DeviceBaseAddress);
741 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->RegionBaseAddress);
742
743 // Convert Fvb
744 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.EraseBlocks);
745 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetAttributes);
746 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetBlockSize);
747 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.GetPhysicalAddress);
748 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Read);
749 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.SetAttributes);
750 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->FvbProtocol.Write);
751
752 if (mNorFlashInstances[Index]->ShadowBuffer != NULL) {
753 EfiConvertPointer (0x0, (VOID **)&mNorFlashInstances[Index]->ShadowBuffer);
754 }
755 }
756
757 return;
758 }