]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - OvmfPkg/QemuFlashFvbServicesRuntimeDxe/QemuFlash.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / OvmfPkg / QemuFlashFvbServicesRuntimeDxe / QemuFlash.c
... / ...
CommitLineData
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
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include <Library/BaseMemoryLib.h>\r
11#include <Library/DebugLib.h>\r
12#include <Library/MemEncryptSevLib.h>\r
13#include <Library/PcdLib.h>\r
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
27UINT8 *mFlashBase;\r
28\r
29STATIC UINTN mFdBlockSize = 0;\r
30STATIC UINTN mFdBlockCount = 0;\r
31\r
32STATIC\r
33volatile UINT8 *\r
34QemuFlashPtr (\r
35 IN EFI_LBA Lba,\r
36 IN UINTN Offset\r
37 )\r
38{\r
39 return mFlashBase + ((UINTN)Lba * mFdBlockSize) + Offset;\r
40}\r
41\r
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
49STATIC\r
50BOOLEAN\r
51QemuFlashDetected (\r
52 VOID\r
53 )\r
54{\r
55 BOOLEAN FlashDetected;\r
56 volatile UINT8 *Ptr;\r
57\r
58 UINTN Offset;\r
59 UINT8 OriginalUint8;\r
60 UINT8 ProbeUint8;\r
61\r
62 FlashDetected = FALSE;\r
63 Ptr = QemuFlashPtr (0, 0);\r
64\r
65 for (Offset = 0; Offset < mFdBlockSize; Offset++) {\r
66 Ptr = QemuFlashPtr (0, Offset);\r
67 ProbeUint8 = *Ptr;\r
68 if ((ProbeUint8 != CLEAR_STATUS_CMD) &&\r
69 (ProbeUint8 != READ_STATUS_CMD) &&\r
70 (ProbeUint8 != CLEARED_ARRAY_STATUS))\r
71 {\r
72 break;\r
73 }\r
74 }\r
75\r
76 if (Offset >= mFdBlockSize) {\r
77 DEBUG ((DEBUG_INFO, "QEMU Flash: Failed to find probe location\n"));\r
78 return FALSE;\r
79 }\r
80\r
81 DEBUG ((DEBUG_INFO, "QEMU Flash: Attempting flash detection at %p\n", Ptr));\r
82\r
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
93 DEBUG ((\r
94 DEBUG_INFO,\r
95 "QEMU Flash: SEV-ES enabled, assuming FD behaves as FLASH\n"\r
96 ));\r
97 return TRUE;\r
98 }\r
99\r
100 OriginalUint8 = *Ptr;\r
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
106 DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));\r
107 *Ptr = OriginalUint8;\r
108 } else {\r
109 *Ptr = READ_STATUS_CMD;\r
110 ProbeUint8 = *Ptr;\r
111 if (ProbeUint8 == OriginalUint8) {\r
112 DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as ROM\n"));\r
113 } else if (ProbeUint8 == READ_STATUS_CMD) {\r
114 DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as RAM\n"));\r
115 *Ptr = OriginalUint8;\r
116 } else if (ProbeUint8 == CLEARED_ARRAY_STATUS) {\r
117 DEBUG ((DEBUG_INFO, "QemuFlashDetected => FD behaves as FLASH\n"));\r
118 FlashDetected = TRUE;\r
119 *Ptr = READ_ARRAY_CMD;\r
120 }\r
121 }\r
122\r
123 DEBUG ((\r
124 DEBUG_INFO,\r
125 "QemuFlashDetected => %a\n",\r
126 FlashDetected ? "Yes" : "No"\r
127 ));\r
128 return FlashDetected;\r
129}\r
130\r
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
141EFI_STATUS\r
142QemuFlashRead (\r
143 IN EFI_LBA Lba,\r
144 IN UINTN Offset,\r
145 IN UINTN *NumBytes,\r
146 IN UINT8 *Buffer\r
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
162 Ptr = (UINT8 *)QemuFlashPtr (Lba, Offset);\r
163\r
164 CopyMem (Buffer, Ptr, *NumBytes);\r
165\r
166 return EFI_SUCCESS;\r
167}\r
168\r
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
179EFI_STATUS\r
180QemuFlashWrite (\r
181 IN EFI_LBA Lba,\r
182 IN UINTN Offset,\r
183 IN UINTN *NumBytes,\r
184 IN UINT8 *Buffer\r
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
203 QemuFlashPtrWrite (Ptr, WRITE_BYTE_CMD);\r
204 QemuFlashPtrWrite (Ptr, Buffer[Loop]);\r
205\r
206 Ptr++;\r
207 }\r
208\r
209 //\r
210 // Restore flash to read mode\r
211 //\r
212 if (*NumBytes > 0) {\r
213 QemuFlashPtrWrite (Ptr - 1, READ_ARRAY_CMD);\r
214 }\r
215\r
216 return EFI_SUCCESS;\r
217}\r
218\r
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
225EFI_STATUS\r
226QemuFlashEraseBlock (\r
227 IN EFI_LBA Lba\r
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
237 QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CMD);\r
238 QemuFlashPtrWrite (Ptr, BLOCK_ERASE_CONFIRM_CMD);\r
239 return EFI_SUCCESS;\r
240}\r
241\r
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
249EFI_STATUS\r
250QemuFlashInitialize (\r
251 VOID\r
252 )\r
253{\r
254 mFlashBase = (UINT8 *)(UINTN)PcdGet32 (PcdOvmfFdBaseAddress);\r
255 mFdBlockSize = PcdGet32 (PcdOvmfFirmwareBlockSize);\r
256 ASSERT (PcdGet32 (PcdOvmfFirmwareFdSize) % mFdBlockSize == 0);\r
257 mFdBlockCount = PcdGet32 (PcdOvmfFirmwareFdSize) / mFdBlockSize;\r
258\r
259 //\r
260 // execute module specific hooks before probing the flash\r
261 //\r
262 QemuFlashBeforeProbe (\r
263 (EFI_PHYSICAL_ADDRESS)(UINTN)mFlashBase,\r
264 mFdBlockSize,\r
265 mFdBlockCount\r
266 );\r
267\r
268 if (!QemuFlashDetected ()) {\r
269 ASSERT (!FeaturePcdGet (PcdSmmSmramRequire));\r
270 return EFI_WRITE_PROTECTED;\r
271 }\r
272\r
273 return EFI_SUCCESS;\r
274}\r