1 /** @file
2   OVMF support for QEMU system firmware flash device
3 
4   Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials are licensed and made available
7   under the terms and conditions of the BSD License which accompanies this
8   distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include <Library/BaseMemoryLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/PcdLib.h>
19 
20 #include "QemuFlash.h"
21 
22 #define WRITE_BYTE_CMD           0x10
23 #define BLOCK_ERASE_CMD          0x20
24 #define CLEAR_STATUS_CMD         0x50
25 #define READ_STATUS_CMD          0x70
26 #define READ_DEVID_CMD           0x90
27 #define BLOCK_ERASE_CONFIRM_CMD  0xd0
28 #define READ_ARRAY_CMD           0xff
29 
30 #define CLEARED_ARRAY_STATUS  0x00
31 
32 
33 UINT8 *mFlashBase;
34 
35 STATIC UINTN       mFdBlockSize = 0;
36 STATIC UINTN       mFdBlockCount = 0;
37 
38 STATIC
39 volatile UINT8*
QemuFlashPtr(IN EFI_LBA Lba,IN UINTN Offset)40 QemuFlashPtr (
41   IN        EFI_LBA                             Lba,
42   IN        UINTN                               Offset
43   )
44 {
45   return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;
46 }
47 
48 
49 /**
50   Determines if the QEMU flash memory device is present.
51 
52   @retval FALSE   The QEMU flash device is not present.
53   @retval TRUE    The QEMU flash device is present.
54 
55 **/
56 STATIC
57 BOOLEAN
QemuFlashDetected(VOID)58 QemuFlashDetected (
59   VOID
60   )
61 {
62   BOOLEAN  FlashDetected;
63   volatile UINT8  *Ptr;
64 
65   UINTN Offset;
66   UINT8 OriginalUint8;
67   UINT8 ProbeUint8;
68 
69   FlashDetected = FALSE;
70   Ptr = QemuFlashPtr (0, 0);
71 
72   for (Offset = 0; Offset < mFdBlockSize; Offset++) {
73     Ptr = QemuFlashPtr (0, Offset);
74     ProbeUint8 = *Ptr;
75     if (ProbeUint8 != CLEAR_STATUS_CMD &&
76         ProbeUint8 != READ_STATUS_CMD &&
77         ProbeUint8 != CLEARED_ARRAY_STATUS) {
78       break;
79     }
80   }
81 
82   if (Offset >= mFdBlockSize) {
83     DEBUG ((EFI_D_INFO, "QEMU Flash: Failed to find probe location\n"));
84     return FALSE;
85   }
86 
87   DEBUG ((EFI_D_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));
88 
89   OriginalUint8 = *Ptr;
90   *Ptr = CLEAR_STATUS_CMD;
91   ProbeUint8 = *Ptr;
92   if (OriginalUint8 != CLEAR_STATUS_CMD &&
93       ProbeUint8 == CLEAR_STATUS_CMD) {
94     DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
95     *Ptr = OriginalUint8;
96   } else {
97     *Ptr = READ_STATUS_CMD;
98     ProbeUint8 = *Ptr;
99     if (ProbeUint8 == OriginalUint8) {
100       DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as ROM\n"));
101     } else if (ProbeUint8 == READ_STATUS_CMD) {
102       DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as RAM\n"));
103       *Ptr = OriginalUint8;
104     } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {
105       DEBUG ((EFI_D_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));
106       FlashDetected = TRUE;
107       *Ptr = READ_ARRAY_CMD;
108     }
109   }
110 
111   DEBUG ((EFI_D_INFO, "QemuFlashDetected => %a\n",
112                       FlashDetected ? "Yes" : "No"));
113   return FlashDetected;
114 }
115 
116 
117 /**
118   Read from QEMU Flash
119 
120   @param[in] Lba      The starting logical block index to read from.
121   @param[in] Offset   Offset into the block at which to begin reading.
122   @param[in] NumBytes On input, indicates the requested read size. On
123                       output, indicates the actual number of bytes read
124   @param[in] Buffer   Pointer to the buffer to read into.
125 
126 **/
127 EFI_STATUS
QemuFlashRead(IN EFI_LBA Lba,IN UINTN Offset,IN UINTN * NumBytes,IN UINT8 * Buffer)128 QemuFlashRead (
129   IN        EFI_LBA                              Lba,
130   IN        UINTN                                Offset,
131   IN        UINTN                                *NumBytes,
132   IN        UINT8                                *Buffer
133   )
134 {
135   UINT8  *Ptr;
136 
137   //
138   // Only write to the first 64k. We don't bother saving the FTW Spare
139   // block into the flash memory.
140   //
141   if (Lba >= mFdBlockCount) {
142     return EFI_INVALID_PARAMETER;
143   }
144 
145   //
146   // Get flash address
147   //
148   Ptr = (UINT8*) QemuFlashPtr (Lba, Offset);
149 
150   CopyMem (Buffer, Ptr, *NumBytes);
151 
152   return EFI_SUCCESS;
153 }
154 
155 
156 /**
157   Write to QEMU Flash
158 
159   @param[in] Lba      The starting logical block index to write to.
160   @param[in] Offset   Offset into the block at which to begin writing.
161   @param[in] NumBytes On input, indicates the requested write size. On
162                       output, indicates the actual number of bytes written
163   @param[in] Buffer   Pointer to the data to write.
164 
165 **/
166 EFI_STATUS
QemuFlashWrite(IN EFI_LBA Lba,IN UINTN Offset,IN UINTN * NumBytes,IN UINT8 * Buffer)167 QemuFlashWrite (
168   IN        EFI_LBA                             Lba,
169   IN        UINTN                               Offset,
170   IN        UINTN                               *NumBytes,
171   IN        UINT8                               *Buffer
172   )
173 {
174   volatile UINT8  *Ptr;
175   UINTN           Loop;
176 
177   //
178   // Only write to the first 64k. We don't bother saving the FTW Spare
179   // block into the flash memory.
180   //
181   if (Lba >= mFdBlockCount) {
182     return EFI_INVALID_PARAMETER;
183   }
184 
185   //
186   // Program flash
187   //
188   Ptr = QemuFlashPtr (Lba, Offset);
189   for (Loop = 0; Loop < *NumBytes; Loop++) {
190     *Ptr = WRITE_BYTE_CMD;
191     *Ptr = Buffer[Loop];
192     Ptr++;
193   }
194 
195   //
196   // Restore flash to read mode
197   //
198   if (*NumBytes > 0) {
199     *(Ptr - 1) = READ_ARRAY_CMD;
200   }
201 
202   return EFI_SUCCESS;
203 }
204 
205 
206 /**
207   Erase a QEMU Flash block
208 
209   @param Lba    The logical block index to erase.
210 
211 **/
212 EFI_STATUS
QemuFlashEraseBlock(IN EFI_LBA Lba)213 QemuFlashEraseBlock (
214   IN   EFI_LBA      Lba
215   )
216 {
217   volatile UINT8  *Ptr;
218 
219   if (Lba >= mFdBlockCount) {
220     return EFI_INVALID_PARAMETER;
221   }
222 
223   Ptr = QemuFlashPtr (Lba, 0);
224   *Ptr = BLOCK_ERASE_CMD;
225   *Ptr = BLOCK_ERASE_CONFIRM_CMD;
226   return EFI_SUCCESS;
227 }
228 
229 
230 /**
231   Initializes QEMU flash memory support
232 
233   @retval EFI_WRITE_PROTECTED   The QEMU flash device is not present.
234   @retval EFI_SUCCESS           The QEMU flash device is supported.
235 
236 **/
237 EFI_STATUS
QemuFlashInitialize(VOID)238 QemuFlashInitialize (
239   VOID
240   )
241 {
242   mFlashBase = (UINT8*)(UINTN) PcdGet32 (PcdOvmfFdBaseAddress);
243   mFdBlockSize = PcdGet32 (PcdOvmfFirmwareBlockSize);
244   ASSERT(PcdGet32 (PcdOvmfFirmwareFdSize) % mFdBlockSize == 0);
245   mFdBlockCount = PcdGet32 (PcdOvmfFirmwareFdSize) / mFdBlockSize;
246 
247   if (!QemuFlashDetected ()) {
248     ASSERT (!FeaturePcdGet (PcdSmmSmramRequire));
249     return EFI_WRITE_PROTECTED;
250   }
251 
252   return EFI_SUCCESS;
253 }
254 
255