• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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