1 /** @file
2   Platform Flash Access library.
3 
4   Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
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 <PiDxe.h>
16 
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/PcdLib.h>
21 #include <Library/PlatformFlashAccessLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Protocol/Spi.h>
24 
25 //
26 // SPI default opcode slots
27 //
28 #define SPI_OPCODE_JEDEC_ID_INDEX        0
29 #define SPI_OPCODE_READ_ID_INDEX         1
30 #define SPI_OPCODE_WRITE_S_INDEX         2
31 #define SPI_OPCODE_WRITE_INDEX           3
32 #define SPI_OPCODE_READ_INDEX            4
33 #define SPI_OPCODE_ERASE_INDEX           5
34 #define SPI_OPCODE_READ_S_INDEX          6
35 #define SPI_OPCODE_CHIP_ERASE_INDEX      7
36 
37 #define SPI_ERASE_SECTOR_SIZE            SIZE_4KB  //This is the chipset requirement
38 
39 STATIC EFI_PHYSICAL_ADDRESS     mInternalFdAddress;
40 EFI_SPI_PROTOCOL                *mSpiProtocol;
41 
42 /**
43   Writes specified number of bytes from the input buffer to the address
44 
45   @param[in]      WriteAddress  The flash address to be written.
46   @param[in, out] NumBytes      The number of bytes.
47   @param[in]      Buffer        The data buffer to be written.
48 
49   @return The status of flash write.
50 **/
51 EFI_STATUS
FlashFdWrite(IN UINTN WriteAddress,IN OUT UINTN * NumBytes,IN UINT8 * Buffer)52 FlashFdWrite (
53   IN  UINTN                               WriteAddress,
54   IN OUT UINTN                            *NumBytes,
55   IN  UINT8                               *Buffer
56   )
57 {
58   EFI_STATUS  Status;
59 
60   Status = EFI_SUCCESS;
61 
62   Status = mSpiProtocol->Execute (
63                            mSpiProtocol,
64                            SPI_OPCODE_WRITE_INDEX, // OpcodeIndex
65                            0,                      // PrefixOpcodeIndex
66                            TRUE,                   // DataCycle
67                            TRUE,                   // Atomic
68                            TRUE,                   // ShiftOut
69                            WriteAddress,           // Address
70                            (UINT32) (*NumBytes),   // Data Number
71                            Buffer,
72                            EnumSpiRegionBios
73                            );
74   DEBUG((DEBUG_INFO, "FlashFdWrite - 0x%x - %r\n", (UINTN)WriteAddress, Status));
75 
76   AsmWbinvd ();
77 
78   return Status;
79 }
80 
81 /**
82   Erase a certain block from address LbaWriteAddress
83 
84   @param[in] WriteAddress  The flash address to be erased.
85 
86   @return The status of flash erase.
87 **/
88 EFI_STATUS
FlashFdErase(IN UINTN WriteAddress)89 FlashFdErase (
90   IN UINTN                                WriteAddress
91   )
92 {
93   EFI_STATUS  Status;
94 
95   Status = mSpiProtocol->Execute (
96                            mSpiProtocol,
97                            SPI_OPCODE_ERASE_INDEX, // OpcodeIndex
98                            0,                      // PrefixOpcodeIndex
99                            FALSE,                  // DataCycle
100                            TRUE,                   // Atomic
101                            FALSE,                  // ShiftOut
102                            WriteAddress,           // Address
103                            0,                      // Data Number
104                            NULL,
105                            EnumSpiRegionBios       // SPI_REGION_TYPE
106                            );
107   DEBUG((DEBUG_INFO, "FlashFdErase - 0x%x - %r\n", (UINTN)WriteAddress, Status));
108 
109   AsmWbinvd ();
110 
111   return Status;
112 }
113 
114 /**
115   Perform flash write opreation.
116 
117   @param[in] FirmwareType      The type of firmware.
118   @param[in] FlashAddress      The address of flash device to be accessed.
119   @param[in] FlashAddressType  The type of flash device address.
120   @param[in] Buffer            The pointer to the data buffer.
121   @param[in] Length            The length of data buffer in bytes.
122 
123   @retval EFI_SUCCESS           The operation returns successfully.
124   @retval EFI_WRITE_PROTECTED   The flash device is read only.
125   @retval EFI_UNSUPPORTED       The flash device access is unsupported.
126   @retval EFI_INVALID_PARAMETER The input parameter is not valid.
127 **/
128 EFI_STATUS
129 EFIAPI
PerformFlashWrite(IN PLATFORM_FIRMWARE_TYPE FirmwareType,IN EFI_PHYSICAL_ADDRESS FlashAddress,IN FLASH_ADDRESS_TYPE FlashAddressType,IN VOID * Buffer,IN UINTN Length)130 PerformFlashWrite (
131   IN PLATFORM_FIRMWARE_TYPE       FirmwareType,
132   IN EFI_PHYSICAL_ADDRESS         FlashAddress,
133   IN FLASH_ADDRESS_TYPE           FlashAddressType,
134   IN VOID                         *Buffer,
135   IN UINTN                        Length
136   )
137 {
138   EFI_STATUS          Status;
139   UINTN               SectorNum;
140   UINTN               Index;
141   UINTN               NumBytes;
142 
143   DEBUG((DEBUG_INFO, "PerformFlashWrite - 0x%x(%x) - 0x%x\n", (UINTN)FlashAddress, (UINTN)FlashAddressType, Length));
144   if (FlashAddressType == FlashAddressTypeAbsoluteAddress) {
145     FlashAddress = FlashAddress - mInternalFdAddress;
146   }
147 
148   //
149   // Erase & Write
150   //
151   SectorNum = Length / SPI_ERASE_SECTOR_SIZE;
152   for (Index = 0; Index < SectorNum; Index++){
153     if (CompareMem(
154           (UINT8 *)(UINTN)(FlashAddress + mInternalFdAddress) + Index * SPI_ERASE_SECTOR_SIZE,
155           (UINT8 *)Buffer + Index * SPI_ERASE_SECTOR_SIZE,
156           SPI_ERASE_SECTOR_SIZE) == 0) {
157       DEBUG((DEBUG_INFO, "Sector - 0x%x - skip\n", Index));
158       continue;
159     }
160     DEBUG((DEBUG_INFO, "Sector - 0x%x - update...\n", Index));
161 
162     Status = FlashFdErase (
163                (UINTN)FlashAddress + Index * SPI_ERASE_SECTOR_SIZE
164                );
165     if (Status != EFI_SUCCESS){
166       break;
167     }
168     NumBytes = SPI_ERASE_SECTOR_SIZE;
169     Status = FlashFdWrite (
170                (UINTN)FlashAddress + Index * SPI_ERASE_SECTOR_SIZE,
171                &NumBytes,
172                (UINT8 *)Buffer + Index * SPI_ERASE_SECTOR_SIZE
173                );
174     if (Status != EFI_SUCCESS){
175       break;
176     }
177   }
178 
179   return EFI_SUCCESS;
180 }
181 
182 /**
183   Platform Flash Access Lib Constructor.
184 
185   @param[in]  ImageHandle       The firmware allocated handle for the EFI image.
186   @param[in]  SystemTable       A pointer to the EFI System Table.
187 
188   @retval EFI_SUCCESS  Constructor returns successfully.
189 **/
190 EFI_STATUS
191 EFIAPI
PerformFlashAccessLibConstructor(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)192 PerformFlashAccessLibConstructor (
193   IN EFI_HANDLE                         ImageHandle,
194   IN EFI_SYSTEM_TABLE                   *SystemTable
195   )
196 {
197   EFI_STATUS  Status;
198 
199   mInternalFdAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32(PcdFlashAreaBaseAddress);
200   DEBUG((DEBUG_INFO, "PcdFlashAreaBaseAddress - 0x%x\n", mInternalFdAddress));
201 
202   Status = gBS->LocateProtocol(&gEfiSpiProtocolGuid, NULL, (VOID **)&mSpiProtocol);
203   ASSERT_EFI_ERROR(Status);
204 
205   return EFI_SUCCESS;
206 }
207