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