1 /** @file
2
3 FV block I/O protocol driver for Styx SPI flash exposed via ISCP
4
5 Copyright (c) 2016, Linaro Ltd. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15 **/
16
17 #include <PiDxe.h>
18
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/DebugLib.h>
21 #include <Library/PcdLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/UefiRuntimeLib.h>
24
25 #include <Protocol/AmdIscpDxeProtocol.h>
26 #include <Protocol/FirmwareVolumeBlock.h>
27
28 #define SPI_BASE (FixedPcdGet64 (PcdFdBaseAddress))
29 #define BLOCK_SIZE (FixedPcdGet32 (PcdFlashNvStorageBlockSize))
30
31 STATIC AMD_ISCP_DXE_PROTOCOL *mIscpDxeProtocol;
32 STATIC EFI_HANDLE mStyxSpiFvHandle;
33
34 STATIC EFI_EVENT mVirtualAddressChangeEvent;
35
36 STATIC UINT64 mNvStorageBase;
37 STATIC UINT64 mNvStorageLbaOffset;
38
39 STATIC CONST UINT64 mNvStorageSize = FixedPcdGet32 (PcdFlashNvStorageVariableSize) +
40 FixedPcdGet32 (PcdFlashNvStorageFtwWorkingSize) +
41 FixedPcdGet32 (PcdFlashNvStorageFtwSpareSize);
42
43
44 /**
45 Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.
46
47 This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
48 It convers pointer to new virtual address.
49
50 @param Event Event whose notification function is being invoked.
51 @param Context Pointer to the notification function's context.
52
53 **/
54 STATIC
55 VOID
56 EFIAPI
VariableClassAddressChangeEvent(IN EFI_EVENT Event,IN VOID * Context)57 VariableClassAddressChangeEvent (
58 IN EFI_EVENT Event,
59 IN VOID *Context
60 )
61 {
62 EfiConvertPointer (0x0, (VOID **)&mIscpDxeProtocol);
63 EfiConvertPointer (0x0, (VOID **)&mNvStorageBase);
64 }
65
66 /**
67 The GetAttributes() function retrieves the attributes and
68 current settings of the block.
69
70 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
71
72 @param Attributes Pointer to EFI_FVB_ATTRIBUTES_2 in which the
73 attributes and current settings are
74 returned. Type EFI_FVB_ATTRIBUTES_2 is defined
75 in EFI_FIRMWARE_VOLUME_HEADER.
76
77 @retval EFI_SUCCESS The firmware volume attributes were
78 returned.
79
80 **/
81 STATIC
82 EFI_STATUS
StyxSpiFvDxeGetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,OUT EFI_FVB_ATTRIBUTES_2 * Attributes)83 StyxSpiFvDxeGetAttributes (
84 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
85 OUT EFI_FVB_ATTRIBUTES_2 *Attributes
86 )
87 {
88 *Attributes = EFI_FVB2_READ_ENABLED_CAP | // Reads may be enabled
89 EFI_FVB2_READ_STATUS | // Reads are currently enabled
90 EFI_FVB2_WRITE_STATUS | // Writes are currently enabled
91 EFI_FVB2_WRITE_ENABLED_CAP | // Writes may be enabled
92 EFI_FVB2_STICKY_WRITE | // A block erase is required to flip bits into EFI_FVB2_ERASE_POLARITY
93 EFI_FVB2_MEMORY_MAPPED | // It is memory mapped
94 EFI_FVB2_ERASE_POLARITY; // After erasure all bits take this value (i.e. '1')
95
96 return EFI_SUCCESS;
97 }
98
99 /**
100 The SetAttributes() function sets configurable firmware volume
101 attributes and returns the new settings of the firmware volume.
102
103 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
104
105 @param Attributes On input, Attributes is a pointer to
106 EFI_FVB_ATTRIBUTES_2 that contains the
107 desired firmware volume settings. On
108 successful return, it contains the new
109 settings of the firmware volume. Type
110 EFI_FVB_ATTRIBUTES_2 is defined in
111 EFI_FIRMWARE_VOLUME_HEADER.
112
113 @retval EFI_SUCCESS The firmware volume attributes were returned.
114
115 @retval EFI_INVALID_PARAMETER The attributes requested are in
116 conflict with the capabilities
117 as declared in the firmware
118 volume header.
119
120 **/
121 STATIC
122 EFI_STATUS
StyxSpiFvDxeSetAttributes(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN OUT EFI_FVB_ATTRIBUTES_2 * Attributes)123 StyxSpiFvDxeSetAttributes (
124 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
125 IN OUT EFI_FVB_ATTRIBUTES_2 *Attributes
126 )
127 {
128 return EFI_SUCCESS; // ignore for now
129 }
130
131 /**
132 The GetPhysicalAddress() function retrieves the base address of
133 a memory-mapped firmware volume. This function should be called
134 only for memory-mapped firmware volumes.
135
136 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
137
138 @param Address Pointer to a caller-allocated
139 EFI_PHYSICAL_ADDRESS that, on successful
140 return from GetPhysicalAddress(), contains the
141 base address of the firmware volume.
142
143 @retval EFI_SUCCESS The firmware volume base address was returned.
144
145 @retval EFI_UNSUPPORTED The firmware volume is not memory mapped.
146
147 **/
148 STATIC
149 EFI_STATUS
StyxSpiFvDxeGetPhysicalAddress(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,OUT EFI_PHYSICAL_ADDRESS * Address)150 StyxSpiFvDxeGetPhysicalAddress (
151 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
152 OUT EFI_PHYSICAL_ADDRESS *Address
153 )
154 {
155 *Address = (EFI_PHYSICAL_ADDRESS)mNvStorageBase;
156 return EFI_SUCCESS;
157 }
158
159 /**
160 The GetBlockSize() function retrieves the size of the requested
161 block. It also returns the number of additional blocks with
162 the identical size. The GetBlockSize() function is used to
163 retrieve the block map (see EFI_FIRMWARE_VOLUME_HEADER).
164
165
166 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
167
168 @param Lba Indicates the block for which to return the size.
169
170 @param BlockSize Pointer to a caller-allocated UINTN in which
171 the size of the block is returned.
172
173 @param NumberOfBlocks Pointer to a caller-allocated UINTN in
174 which the number of consecutive blocks,
175 starting with Lba, is returned. All
176 blocks in this range have a size of
177 BlockSize.
178
179
180 @retval EFI_SUCCESS The firmware volume base address was returned.
181
182 @retval EFI_INVALID_PARAMETER The requested LBA is out of range.
183
184 **/
185 STATIC
186 EFI_STATUS
StyxSpiFvDxeGetBlockSize(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,OUT UINTN * BlockSize,OUT UINTN * NumberOfBlocks)187 StyxSpiFvDxeGetBlockSize (
188 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
189 IN EFI_LBA Lba,
190 OUT UINTN *BlockSize,
191 OUT UINTN *NumberOfBlocks
192 )
193 {
194 *BlockSize = BLOCK_SIZE;
195 *NumberOfBlocks = mNvStorageSize / BLOCK_SIZE - (UINTN)Lba;
196
197 return EFI_SUCCESS;
198 }
199
200 /**
201 Reads the specified number of bytes into a buffer from the specified block.
202
203 The Read() function reads the requested number of bytes from the
204 requested block and stores them in the provided buffer.
205 Implementations should be mindful that the firmware volume
206 might be in the ReadDisabled state. If it is in this state,
207 the Read() function must return the status code
208 EFI_ACCESS_DENIED without modifying the contents of the
209 buffer. The Read() function must also prevent spanning block
210 boundaries. If a read is requested that would span a block
211 boundary, the read must read up to the boundary but not
212 beyond. The output parameter NumBytes must be set to correctly
213 indicate the number of bytes actually read. The caller must be
214 aware that a read may be partially completed.
215
216 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
217
218 @param Lba The starting logical block index
219 from which to read.
220
221 @param Offset Offset into the block at which to begin reading.
222
223 @param NumBytes Pointer to a UINTN. At entry, *NumBytes
224 contains the total size of the buffer. At
225 exit, *NumBytes contains the total number of
226 bytes read.
227
228 @param Buffer Pointer to a caller-allocated buffer that will
229 be used to hold the data that is read.
230
231 @retval EFI_SUCCESS The firmware volume was read successfully,
232 and contents are in Buffer.
233
234 @retval EFI_BAD_BUFFER_SIZE Read attempted across an LBA
235 boundary. On output, NumBytes
236 contains the total number of bytes
237 returned in Buffer.
238
239 @retval EFI_ACCESS_DENIED The firmware volume is in the
240 ReadDisabled state.
241
242 @retval EFI_DEVICE_ERROR The block device is not
243 functioning correctly and could
244 not be read.
245
246 **/
247 STATIC
248 EFI_STATUS
StyxSpiFvDxeRead(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN OUT UINT8 * Buffer)249 StyxSpiFvDxeRead (
250 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
251 IN EFI_LBA Lba,
252 IN UINTN Offset,
253 IN OUT UINTN *NumBytes,
254 IN OUT UINT8 *Buffer
255 )
256 {
257 VOID *Base;
258
259 if (Offset + *NumBytes > BLOCK_SIZE) {
260 return EFI_BAD_BUFFER_SIZE;
261 }
262
263 Base = (VOID *)mNvStorageBase + Lba * BLOCK_SIZE + Offset;
264
265 //
266 // Copy the data from the in-memory image
267 //
268 CopyMem (Buffer, Base, *NumBytes);
269
270 DEBUG_CODE_BEGIN ();
271 EFI_STATUS Status;
272
273 if (!EfiAtRuntime ()) {
274 Lba += mNvStorageLbaOffset;
275 Status = mIscpDxeProtocol->AmdExecuteLoadFvBlockDxe (mIscpDxeProtocol,
276 Lba * BLOCK_SIZE + Offset, Buffer, *NumBytes);
277 ASSERT_EFI_ERROR (Status);
278
279 ASSERT (CompareMem (Base, Buffer, *NumBytes) == 0);
280 }
281 DEBUG_CODE_END ();
282
283 return EFI_SUCCESS;
284 }
285
286 /**
287 Writes the specified number of bytes from the input buffer to the block.
288
289 The Write() function writes the specified number of bytes from
290 the provided buffer to the specified block and offset. If the
291 firmware volume is sticky write, the caller must ensure that
292 all the bits of the specified range to write are in the
293 EFI_FVB_ERASE_POLARITY state before calling the Write()
294 function, or else the result will be unpredictable. This
295 unpredictability arises because, for a sticky-write firmware
296 volume, a write may negate a bit in the EFI_FVB_ERASE_POLARITY
297 state but cannot flip it back again. Before calling the
298 Write() function, it is recommended for the caller to first call
299 the EraseBlocks() function to erase the specified block to
300 write. A block erase cycle will transition bits from the
301 (NOT)EFI_FVB_ERASE_POLARITY state back to the
302 EFI_FVB_ERASE_POLARITY state. Implementations should be
303 mindful that the firmware volume might be in the WriteDisabled
304 state. If it is in this state, the Write() function must
305 return the status code EFI_ACCESS_DENIED without modifying the
306 contents of the firmware volume. The Write() function must
307 also prevent spanning block boundaries. If a write is
308 requested that spans a block boundary, the write must store up
309 to the boundary but not beyond. The output parameter NumBytes
310 must be set to correctly indicate the number of bytes actually
311 written. The caller must be aware that a write may be
312 partially completed. All writes, partial or otherwise, must be
313 fully flushed to the hardware before the Write() service
314 returns.
315
316 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL instance.
317
318 @param Lba The starting logical block index to write to.
319
320 @param Offset Offset into the block at which to begin writing.
321
322 @param NumBytes The pointer to a UINTN. At entry, *NumBytes
323 contains the total size of the buffer. At
324 exit, *NumBytes contains the total number of
325 bytes actually written.
326
327 @param Buffer The pointer to a caller-allocated buffer that
328 contains the source for the write.
329
330 @retval EFI_SUCCESS The firmware volume was written successfully.
331
332 @retval EFI_BAD_BUFFER_SIZE The write was attempted across an
333 LBA boundary. On output, NumBytes
334 contains the total number of bytes
335 actually written.
336
337 @retval EFI_ACCESS_DENIED The firmware volume is in the
338 WriteDisabled state.
339
340 @retval EFI_DEVICE_ERROR The block device is malfunctioning
341 and could not be written.
342
343
344 **/
345 STATIC
346 EFI_STATUS
StyxSpiFvDxeWrite(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,IN EFI_LBA Lba,IN UINTN Offset,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)347 StyxSpiFvDxeWrite (
348 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
349 IN EFI_LBA Lba,
350 IN UINTN Offset,
351 IN OUT UINTN *NumBytes,
352 IN UINT8 *Buffer
353 )
354 {
355 EFI_STATUS Status;
356 VOID *Base;
357
358 if (Offset + *NumBytes > BLOCK_SIZE) {
359 return EFI_BAD_BUFFER_SIZE;
360 }
361
362 Base = (VOID *)mNvStorageBase + Lba * BLOCK_SIZE + Offset;
363
364 Lba += mNvStorageLbaOffset;
365 Status = mIscpDxeProtocol->AmdExecuteUpdateFvBlockDxe (mIscpDxeProtocol,
366 Lba * BLOCK_SIZE + Offset, Buffer, *NumBytes);
367 if (!EFI_ERROR (Status)) {
368 //
369 // Copy the data we just wrote to the in-memory copy of the
370 // firmware volume
371 //
372 CopyMem (Base, Buffer, *NumBytes);
373 }
374 return Status;
375 }
376
377 /**
378 Erases and initializes a firmware volume block.
379
380 The EraseBlocks() function erases one or more blocks as denoted
381 by the variable argument list. The entire parameter list of
382 blocks must be verified before erasing any blocks. If a block is
383 requested that does not exist within the associated firmware
384 volume (it has a larger index than the last block of the
385 firmware volume), the EraseBlocks() function must return the
386 status code EFI_INVALID_PARAMETER without modifying the contents
387 of the firmware volume. Implementations should be mindful that
388 the firmware volume might be in the WriteDisabled state. If it
389 is in this state, the EraseBlocks() function must return the
390 status code EFI_ACCESS_DENIED without modifying the contents of
391 the firmware volume. All calls to EraseBlocks() must be fully
392 flushed to the hardware before the EraseBlocks() service
393 returns.
394
395 @param This Indicates the EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL
396 instance.
397
398 @param ... The variable argument list is a list of tuples.
399 Each tuple describes a range of LBAs to erase
400 and consists of the following:
401 - An EFI_LBA that indicates the starting LBA
402 - A UINTN that indicates the number of blocks to
403 erase.
404
405 The list is terminated with an
406 EFI_LBA_LIST_TERMINATOR. For example, the
407 following indicates that two ranges of blocks
408 (5-7 and 10-11) are to be erased: EraseBlocks
409 (This, 5, 3, 10, 2, EFI_LBA_LIST_TERMINATOR);
410
411 @retval EFI_SUCCESS The erase request successfully
412 completed.
413
414 @retval EFI_ACCESS_DENIED The firmware volume is in the
415 WriteDisabled state.
416 @retval EFI_DEVICE_ERROR The block device is not functioning
417 correctly and could not be written.
418 The firmware device may have been
419 partially erased.
420 @retval EFI_INVALID_PARAMETER One or more of the LBAs listed
421 in the variable argument list do
422 not exist in the firmware volume.
423
424 **/
425 STATIC
426 EFI_STATUS
StyxSpiFvDxeErase(IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL * This,...)427 StyxSpiFvDxeErase (
428 IN CONST EFI_FIRMWARE_VOLUME_BLOCK2_PROTOCOL *This,
429 ...
430 )
431 {
432 VA_LIST Args;
433 EFI_LBA Start;
434 UINTN Length;
435 EFI_STATUS Status;
436
437 VA_START (Args, This);
438
439 for (Start = VA_ARG (Args, EFI_LBA);
440 Start != EFI_LBA_LIST_TERMINATOR;
441 Start = VA_ARG (Args, EFI_LBA)) {
442 Start += mNvStorageLbaOffset;
443 Length = VA_ARG (Args, UINTN);
444 Status = mIscpDxeProtocol->AmdExecuteEraseFvBlockDxe (mIscpDxeProtocol,
445 (Start + mNvStorageLbaOffset) * BLOCK_SIZE,
446 Length * BLOCK_SIZE);
447 if (!EFI_ERROR (Status)) {
448 SetMem64 ((VOID *)mNvStorageBase + Start * BLOCK_SIZE,
449 Length * BLOCK_SIZE, ~0UL);
450 }
451 }
452
453 VA_END (Args);
454
455 return EFI_SUCCESS;
456 }
457
458 STATIC
459 EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL mStyxSpiFvProtocol = {
460 StyxSpiFvDxeGetAttributes,
461 StyxSpiFvDxeSetAttributes,
462 StyxSpiFvDxeGetPhysicalAddress,
463 StyxSpiFvDxeGetBlockSize,
464 StyxSpiFvDxeRead,
465 StyxSpiFvDxeWrite,
466 StyxSpiFvDxeErase
467 };
468
469 EFI_STATUS
470 EFIAPI
StyxSpiFvDxeInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)471 StyxSpiFvDxeInitialize (
472 IN EFI_HANDLE ImageHandle,
473 IN EFI_SYSTEM_TABLE *SystemTable
474 )
475 {
476 EFI_STATUS Status;
477
478 mNvStorageBase = PcdGet64 (PcdFlashNvStorageVariableBase64);
479 mNvStorageLbaOffset = (FixedPcdGet64 (PcdFlashNvStorageOriginalBase) -
480 SPI_BASE) / BLOCK_SIZE;
481
482 DEBUG ((EFI_D_INFO,
483 "%a: Using NV store FV in-memory copy at 0x%lx, LBA offset == 0x%lx\n",
484 __FUNCTION__, mNvStorageBase, mNvStorageLbaOffset));
485
486 Status = gBS->LocateProtocol (&gAmdIscpDxeProtocolGuid, NULL,
487 (VOID **)&mIscpDxeProtocol);
488 if (EFI_ERROR (Status)) {
489 return Status;
490 }
491
492 Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
493 VariableClassAddressChangeEvent, NULL,
494 &gEfiEventVirtualAddressChangeGuid,
495 &mVirtualAddressChangeEvent);
496 ASSERT_EFI_ERROR (Status);
497
498 return gBS->InstallMultipleProtocolInterfaces (&mStyxSpiFvHandle,
499 &gEfiFirmwareVolumeBlockProtocolGuid, &mStyxSpiFvProtocol,
500 NULL);
501 }
502