]>
Commit | Line | Data |
---|---|---|
a4ce9ffd JJ |
1 | /** @file\r |
2 | OVMF support for QEMU system firmware flash device\r | |
3 | \r | |
4 | Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.<BR>\r | |
ea0d111e | 5 | \r |
b26f0cf9 | 6 | SPDX-License-Identifier: BSD-2-Clause-Patent\r |
a4ce9ffd JJ |
7 | \r |
8 | **/\r | |
9 | \r | |
a4ce9ffd | 10 | #include <Library/BaseMemoryLib.h>\r |
0f2eb31c | 11 | #include <Library/DebugLib.h>\r |
437eb3f7 | 12 | #include <Library/MemEncryptSevLib.h>\r |
a4ce9ffd | 13 | #include <Library/PcdLib.h>\r |
a4ce9ffd JJ |
14 | \r |
15 | #include "QemuFlash.h"\r | |
16 | \r | |
17 | #define WRITE_BYTE_CMD 0x10\r | |
18 | #define BLOCK_ERASE_CMD 0x20\r | |
19 | #define CLEAR_STATUS_CMD 0x50\r | |
20 | #define READ_STATUS_CMD 0x70\r | |
21 | #define READ_DEVID_CMD 0x90\r | |
22 | #define BLOCK_ERASE_CONFIRM_CMD 0xd0\r | |
23 | #define READ_ARRAY_CMD 0xff\r | |
24 | \r | |
25 | #define CLEARED_ARRAY_STATUS 0x00\r | |
26 | \r | |
ac0a286f | 27 | UINT8 *mFlashBase;\r |
a4ce9ffd | 28 | \r |
ac0a286f MK |
29 | STATIC UINTN mFdBlockSize = 0;\r |
30 | STATIC UINTN mFdBlockCount = 0;\r | |
a4ce9ffd | 31 | \r |
a4ce9ffd | 32 | STATIC\r |
ac0a286f | 33 | volatile UINT8 *\r |
a4ce9ffd | 34 | QemuFlashPtr (\r |
ac0a286f MK |
35 | IN EFI_LBA Lba,\r |
36 | IN UINTN Offset\r | |
a4ce9ffd JJ |
37 | )\r |
38 | {\r | |
f7e899c7 | 39 | return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;\r |
a4ce9ffd JJ |
40 | }\r |
41 | \r | |
a4ce9ffd JJ |
42 | /**\r |
43 | Determines if the QEMU flash memory device is present.\r | |
44 | \r | |
45 | @retval FALSE The QEMU flash device is not present.\r | |
46 | @retval TRUE The QEMU flash device is present.\r | |
47 | \r | |
48 | **/\r | |
49 | STATIC\r | |
50 | BOOLEAN\r | |
51 | QemuFlashDetected (\r | |
52 | VOID\r | |
53 | )\r | |
54 | {\r | |
ac0a286f | 55 | BOOLEAN FlashDetected;\r |
a4ce9ffd JJ |
56 | volatile UINT8 *Ptr;\r |
57 | \r | |
ac0a286f MK |
58 | UINTN Offset;\r |
59 | UINT8 OriginalUint8;\r | |
60 | UINT8 ProbeUint8;\r | |
a4ce9ffd JJ |
61 | \r |
62 | FlashDetected = FALSE;\r | |
ac0a286f | 63 | Ptr = QemuFlashPtr (0, 0);\r |
a4ce9ffd JJ |
64 | \r |
65 | for (Offset = 0; Offset < mFdBlockSize; Offset++) {\r | |
ac0a286f | 66 | Ptr = QemuFlashPtr (0, Offset);\r |
a4ce9ffd | 67 | ProbeUint8 = *Ptr;\r |
ac0a286f MK |
68 | if ((ProbeUint8 != CLEAR_STATUS_CMD) &&\r |
69 | (ProbeUint8 != READ_STATUS_CMD) &&\r | |
70 | (ProbeUint8 != CLEARED_ARRAY_STATUS))\r | |
71 | {\r | |
a4ce9ffd JJ |
72 | break;\r |
73 | }\r | |
74 | }\r | |
75 | \r | |
76 | if (Offset >= mFdBlockSize) {\r | |
70d5086c | 77 | DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));\r |
a4ce9ffd JJ |
78 | return FALSE;\r |
79 | }\r | |
80 | \r | |
70d5086c | 81 | DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));\r |
a4ce9ffd | 82 | \r |
437eb3f7 TL |
83 | if (MemEncryptSevEsIsEnabled ()) {\r |
84 | //\r | |
85 | // When SEV-ES is enabled, the check below can result in an infinite\r | |
86 | // loop with respect to a nested page fault. When the memslot is mapped\r | |
87 | // read-only, the nested page table entry is read-only. The check below\r | |
88 | // will cause a nested page fault that cannot be emulated, causing\r | |
89 | // the instruction to retried over and over. For SEV-ES, acknowledge that\r | |
90 | // the FD appears as ROM and not as FLASH, but report FLASH anyway because\r | |
91 | // FLASH behavior can be simulated using VMGEXIT.\r | |
92 | //\r | |
ac0a286f MK |
93 | DEBUG ((\r |
94 | DEBUG_INFO,\r | |
95 | "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"\r | |
96 | ));\r | |
437eb3f7 TL |
97 | return TRUE;\r |
98 | }\r | |
99 | \r | |
a4ce9ffd | 100 | OriginalUint8 = *Ptr;\r |
ac0a286f MK |
101 | *Ptr = CLEAR_STATUS_CMD;\r |
102 | ProbeUint8 = *Ptr;\r | |
103 | if ((OriginalUint8 != CLEAR_STATUS_CMD) &&\r | |
104 | (ProbeUint8 == CLEAR_STATUS_CMD))\r | |
105 | {\r | |
70d5086c | 106 | DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));\r |
a4ce9ffd JJ |
107 | *Ptr = OriginalUint8;\r |
108 | } else {\r | |
ac0a286f | 109 | *Ptr = READ_STATUS_CMD;\r |
a4ce9ffd JJ |
110 | ProbeUint8 = *Ptr;\r |
111 | if (ProbeUint8 == OriginalUint8) {\r | |
70d5086c | 112 | DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));\r |
a4ce9ffd | 113 | } else if (ProbeUint8 == READ_STATUS_CMD) {\r |
70d5086c | 114 | DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));\r |
a4ce9ffd JJ |
115 | *Ptr = OriginalUint8;\r |
116 | } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {\r | |
70d5086c | 117 | DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));\r |
a4ce9ffd | 118 | FlashDetected = TRUE;\r |
ac0a286f | 119 | *Ptr = READ_ARRAY_CMD;\r |
a4ce9ffd JJ |
120 | }\r |
121 | }\r | |
122 | \r | |
ac0a286f MK |
123 | DEBUG ((\r |
124 | DEBUG_INFO,\r | |
125 | "QemuFlashDetected => %a\n",\r | |
126 | FlashDetected ? "Yes" : "No"\r | |
127 | ));\r | |
a4ce9ffd JJ |
128 | return FlashDetected;\r |
129 | }\r | |
130 | \r | |
a4ce9ffd JJ |
131 | /**\r |
132 | Read from QEMU Flash\r | |
133 | \r | |
134 | @param[in] Lba The starting logical block index to read from.\r | |
135 | @param[in] Offset Offset into the block at which to begin reading.\r | |
136 | @param[in] NumBytes On input, indicates the requested read size. On\r | |
137 | output, indicates the actual number of bytes read\r | |
138 | @param[in] Buffer Pointer to the buffer to read into.\r | |
139 | \r | |
140 | **/\r | |
141 | EFI_STATUS\r | |
142 | QemuFlashRead (\r | |
ac0a286f MK |
143 | IN EFI_LBA Lba,\r |
144 | IN UINTN Offset,\r | |
145 | IN UINTN *NumBytes,\r | |
146 | IN UINT8 *Buffer\r | |
a4ce9ffd JJ |
147 | )\r |
148 | {\r | |
149 | UINT8 *Ptr;\r | |
150 | \r | |
151 | //\r | |
152 | // Only write to the first 64k. We don't bother saving the FTW Spare\r | |
153 | // block into the flash memory.\r | |
154 | //\r | |
155 | if (Lba >= mFdBlockCount) {\r | |
156 | return EFI_INVALID_PARAMETER;\r | |
157 | }\r | |
158 | \r | |
159 | //\r | |
160 | // Get flash address\r | |
161 | //\r | |
ac0a286f | 162 | Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);\r |
a4ce9ffd JJ |
163 | \r |
164 | CopyMem (Buffer, Ptr, *NumBytes);\r | |
165 | \r | |
166 | return EFI_SUCCESS;\r | |
167 | }\r | |
168 | \r | |
a4ce9ffd JJ |
169 | /**\r |
170 | Write to QEMU Flash\r | |
171 | \r | |
172 | @param[in] Lba The starting logical block index to write to.\r | |
173 | @param[in] Offset Offset into the block at which to begin writing.\r | |
174 | @param[in] NumBytes On input, indicates the requested write size. On\r | |
175 | output, indicates the actual number of bytes written\r | |
176 | @param[in] Buffer Pointer to the data to write.\r | |
177 | \r | |
178 | **/\r | |
179 | EFI_STATUS\r | |
180 | QemuFlashWrite (\r | |
ac0a286f MK |
181 | IN EFI_LBA Lba,\r |
182 | IN UINTN Offset,\r | |
183 | IN UINTN *NumBytes,\r | |
184 | IN UINT8 *Buffer\r | |
a4ce9ffd JJ |
185 | )\r |
186 | {\r | |
187 | volatile UINT8 *Ptr;\r | |
188 | UINTN Loop;\r | |
189 | \r | |
190 | //\r | |
191 | // Only write to the first 64k. We don't bother saving the FTW Spare\r | |
192 | // block into the flash memory.\r | |
193 | //\r | |
194 | if (Lba >= mFdBlockCount) {\r | |
195 | return EFI_INVALID_PARAMETER;\r | |
196 | }\r | |
197 | \r | |
198 | //\r | |
199 | // Program flash\r | |
200 | //\r | |
201 | Ptr = QemuFlashPtr (Lba, Offset);\r | |
202 | for (Loop = 0; Loop < *NumBytes; Loop++) {\r | |
437eb3f7 TL |
203 | QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);\r |
204 | QemuFlashPtrWrite (Ptr, Buffer[Loop]);\r | |
205 | \r | |
a4ce9ffd JJ |
206 | Ptr++;\r |
207 | }\r | |
208 | \r | |
209 | //\r | |
210 | // Restore flash to read mode\r | |
211 | //\r | |
212 | if (*NumBytes > 0) {\r | |
437eb3f7 | 213 | QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);\r |
a4ce9ffd JJ |
214 | }\r |
215 | \r | |
216 | return EFI_SUCCESS;\r | |
217 | }\r | |
218 | \r | |
a4ce9ffd JJ |
219 | /**\r |
220 | Erase a QEMU Flash block\r | |
221 | \r | |
222 | @param Lba The logical block index to erase.\r | |
223 | \r | |
224 | **/\r | |
225 | EFI_STATUS\r | |
226 | QemuFlashEraseBlock (\r | |
ac0a286f | 227 | IN EFI_LBA Lba\r |
a4ce9ffd JJ |
228 | )\r |
229 | {\r | |
230 | volatile UINT8 *Ptr;\r | |
231 | \r | |
232 | if (Lba >= mFdBlockCount) {\r | |
233 | return EFI_INVALID_PARAMETER;\r | |
234 | }\r | |
235 | \r | |
236 | Ptr = QemuFlashPtr (Lba, 0);\r | |
fdce1122 TL |
237 | QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CMD);\r |
238 | QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CONFIRM_CMD);\r | |
a4ce9ffd JJ |
239 | return EFI_SUCCESS;\r |
240 | }\r | |
241 | \r | |
a4ce9ffd JJ |
242 | /**\r |
243 | Initializes QEMU flash memory support\r | |
244 | \r | |
245 | @retval EFI_WRITE_PROTECTED The QEMU flash device is not present.\r | |
246 | @retval EFI_SUCCESS The QEMU flash device is supported.\r | |
247 | \r | |
248 | **/\r | |
249 | EFI_STATUS\r | |
250 | QemuFlashInitialize (\r | |
251 | VOID\r | |
252 | )\r | |
253 | {\r | |
ac0a286f | 254 | mFlashBase = (UINT8 *)(UINTN)PcdGet32 (PcdOvmfFdBaseAddress);\r |
a4ce9ffd | 255 | mFdBlockSize = PcdGet32 (PcdOvmfFirmwareBlockSize);\r |
ac0a286f | 256 | ASSERT (PcdGet32 (PcdOvmfFirmwareFdSize) % mFdBlockSize == 0);\r |
a4ce9ffd JJ |
257 | mFdBlockCount = PcdGet32 (PcdOvmfFirmwareFdSize) / mFdBlockSize;\r |
258 | \r | |
e4a1d5a7 BS |
259 | //\r |
260 | // execute module specific hooks before probing the flash\r | |
261 | //\r | |
262 | QemuFlashBeforeProbe (\r | |
ac0a286f | 263 | (EFI_PHYSICAL_ADDRESS)(UINTN)mFlashBase,\r |
e4a1d5a7 BS |
264 | mFdBlockSize,\r |
265 | mFdBlockCount\r | |
266 | );\r | |
267 | \r | |
a4ce9ffd | 268 | if (!QemuFlashDetected ()) {\r |
b963ec49 | 269 | ASSERT (!FeaturePcdGet (PcdSmmSmramRequire));\r |
a4ce9ffd JJ |
270 | return EFI_WRITE_PROTECTED;\r |
271 | }\r | |
272 | \r | |
273 | return EFI_SUCCESS;\r | |
274 | }\r |