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