]>
Commit | Line | Data |
---|---|---|
3b96221f AP |
1 | /**@file\r |
2 | Memory Detection for Virtual Machines.\r | |
3 | \r | |
4 | Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r | |
5 | Copyright (c) 2019, Citrix Systems, Inc.\r | |
6 | \r | |
7 | SPDX-License-Identifier: BSD-2-Clause-Patent\r | |
8 | \r | |
9 | Module Name:\r | |
10 | \r | |
11 | MemDetect.c\r | |
12 | \r | |
13 | **/\r | |
14 | \r | |
15 | //\r | |
16 | // The package level header files this module uses\r | |
17 | //\r | |
18 | #include <IndustryStandard/Q35MchIch9.h>\r | |
19 | #include <PiPei.h>\r | |
20 | \r | |
21 | //\r | |
22 | // The Library classes this module consumes\r | |
23 | //\r | |
24 | #include <Library/BaseLib.h>\r | |
25 | #include <Library/BaseMemoryLib.h>\r | |
26 | #include <Library/DebugLib.h>\r | |
27 | #include <Library/HobLib.h>\r | |
28 | #include <Library/IoLib.h>\r | |
29 | #include <Library/PcdLib.h>\r | |
30 | #include <Library/PciLib.h>\r | |
31 | #include <Library/PeimEntryPoint.h>\r | |
32 | #include <Library/ResourcePublicationLib.h>\r | |
33 | \r | |
34 | #include "Platform.h"\r | |
35 | #include "Cmos.h"\r | |
36 | \r | |
ac0a286f | 37 | UINT8 mPhysMemAddressWidth;\r |
3b96221f | 38 | \r |
ac0a286f MK |
39 | STATIC UINT32 mS3AcpiReservedMemoryBase;\r |
40 | STATIC UINT32 mS3AcpiReservedMemorySize;\r | |
3b96221f | 41 | \r |
ac0a286f | 42 | STATIC UINT16 mQ35TsegMbytes;\r |
3b96221f AP |
43 | \r |
44 | VOID\r | |
45 | Q35TsegMbytesInitialization (\r | |
46 | VOID\r | |
47 | )\r | |
48 | {\r | |
ac0a286f MK |
49 | UINT16 ExtendedTsegMbytes;\r |
50 | RETURN_STATUS PcdStatus;\r | |
3b96221f AP |
51 | \r |
52 | if (mHostBridgeDevId != INTEL_Q35_MCH_DEVICE_ID) {\r | |
53 | DEBUG ((\r | |
54 | DEBUG_ERROR,\r | |
55 | "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "\r | |
56 | "only DID=0x%04x (Q35) is supported\n",\r | |
57 | __FUNCTION__,\r | |
58 | mHostBridgeDevId,\r | |
59 | INTEL_Q35_MCH_DEVICE_ID\r | |
60 | ));\r | |
61 | ASSERT (FALSE);\r | |
62 | CpuDeadLoop ();\r | |
63 | }\r | |
64 | \r | |
65 | //\r | |
66 | // Check if QEMU offers an extended TSEG.\r | |
67 | //\r | |
68 | // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB\r | |
69 | // register, and reading back the register.\r | |
70 | //\r | |
71 | // On a QEMU machine type that does not offer an extended TSEG, the initial\r | |
72 | // write overwrites whatever value a malicious guest OS may have placed in\r | |
73 | // the (unimplemented) register, before entering S3 or rebooting.\r | |
74 | // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.\r | |
75 | //\r | |
76 | // On a QEMU machine type that offers an extended TSEG, the initial write\r | |
77 | // triggers an update to the register. Subsequently, the value read back\r | |
78 | // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the\r | |
79 | // number of megabytes.\r | |
80 | //\r | |
81 | PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);\r | |
82 | ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));\r | |
83 | if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {\r | |
84 | mQ35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);\r | |
85 | return;\r | |
86 | }\r | |
87 | \r | |
88 | DEBUG ((\r | |
89 | DEBUG_INFO,\r | |
90 | "%a: QEMU offers an extended TSEG (%d MB)\n",\r | |
91 | __FUNCTION__,\r | |
92 | ExtendedTsegMbytes\r | |
93 | ));\r | |
94 | PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);\r | |
95 | ASSERT_RETURN_ERROR (PcdStatus);\r | |
96 | mQ35TsegMbytes = ExtendedTsegMbytes;\r | |
97 | }\r | |
98 | \r | |
24465c38 AP |
99 | STATIC\r |
100 | UINT64\r | |
101 | GetHighestSystemMemoryAddress (\r | |
ac0a286f | 102 | BOOLEAN Below4gb\r |
24465c38 AP |
103 | )\r |
104 | {\r | |
ac0a286f MK |
105 | EFI_E820_ENTRY64 *E820Map;\r |
106 | UINT32 E820EntriesCount;\r | |
107 | EFI_E820_ENTRY64 *Entry;\r | |
108 | EFI_STATUS Status;\r | |
109 | UINT32 Loop;\r | |
110 | UINT64 HighestAddress;\r | |
111 | UINT64 EntryEnd;\r | |
24465c38 AP |
112 | \r |
113 | HighestAddress = 0;\r | |
114 | \r | |
115 | Status = XenGetE820Map (&E820Map, &E820EntriesCount);\r | |
116 | ASSERT_EFI_ERROR (Status);\r | |
117 | \r | |
118 | for (Loop = 0; Loop < E820EntriesCount; Loop++) {\r | |
ac0a286f | 119 | Entry = E820Map + Loop;\r |
24465c38 AP |
120 | EntryEnd = Entry->BaseAddr + Entry->Length;\r |
121 | \r | |
ac0a286f MK |
122 | if ((Entry->Type == EfiAcpiAddressRangeMemory) &&\r |
123 | (EntryEnd > HighestAddress))\r | |
124 | {\r | |
24465c38 AP |
125 | if (Below4gb && (EntryEnd <= BASE_4GB)) {\r |
126 | HighestAddress = EntryEnd;\r | |
127 | } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {\r | |
128 | HighestAddress = EntryEnd;\r | |
129 | }\r | |
130 | }\r | |
131 | }\r | |
132 | \r | |
133 | //\r | |
134 | // Round down the end address.\r | |
135 | //\r | |
136 | return HighestAddress & ~(UINT64)EFI_PAGE_MASK;\r | |
137 | }\r | |
3b96221f AP |
138 | \r |
139 | UINT32\r | |
140 | GetSystemMemorySizeBelow4gb (\r | |
141 | VOID\r | |
142 | )\r | |
143 | {\r | |
ac0a286f MK |
144 | UINT8 Cmos0x34;\r |
145 | UINT8 Cmos0x35;\r | |
3b96221f | 146 | \r |
24465c38 AP |
147 | //\r |
148 | // In PVH case, there is no CMOS, we have to calculate the memory size\r | |
149 | // from parsing the E820\r | |
150 | //\r | |
151 | if (XenPvhDetected ()) {\r | |
152 | UINT64 HighestAddress;\r | |
153 | \r | |
154 | HighestAddress = GetHighestSystemMemoryAddress (TRUE);\r | |
155 | ASSERT (HighestAddress > 0 && HighestAddress <= BASE_4GB);\r | |
156 | \r | |
a9255967 | 157 | return (UINT32)HighestAddress;\r |
24465c38 AP |
158 | }\r |
159 | \r | |
3b96221f AP |
160 | //\r |
161 | // CMOS 0x34/0x35 specifies the system memory above 16 MB.\r | |
162 | // * CMOS(0x35) is the high byte\r | |
163 | // * CMOS(0x34) is the low byte\r | |
164 | // * The size is specified in 64kb chunks\r | |
165 | // * Since this is memory above 16MB, the 16MB must be added\r | |
166 | // into the calculation to get the total memory size.\r | |
167 | //\r | |
168 | \r | |
ac0a286f MK |
169 | Cmos0x34 = (UINT8)CmosRead8 (0x34);\r |
170 | Cmos0x35 = (UINT8)CmosRead8 (0x35);\r | |
3b96221f | 171 | \r |
ac0a286f | 172 | return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r |
3b96221f AP |
173 | }\r |
174 | \r | |
3b96221f | 175 | /**\r |
e68c2a22 | 176 | Initialize the mPhysMemAddressWidth variable, based on CPUID data.\r |
3b96221f AP |
177 | **/\r |
178 | VOID\r | |
179 | AddressWidthInitialization (\r | |
180 | VOID\r | |
181 | )\r | |
182 | {\r | |
ac0a286f | 183 | UINT32 RegEax;\r |
3b96221f | 184 | \r |
e68c2a22 ID |
185 | AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r |
186 | if (RegEax >= 0x80000008) {\r | |
187 | AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r | |
ac0a286f | 188 | mPhysMemAddressWidth = (UINT8)RegEax;\r |
e68c2a22 ID |
189 | } else {\r |
190 | mPhysMemAddressWidth = 36;\r | |
3b96221f AP |
191 | }\r |
192 | \r | |
193 | //\r | |
e68c2a22 | 194 | // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r |
3b96221f | 195 | //\r |
e68c2a22 ID |
196 | ASSERT (mPhysMemAddressWidth <= 52);\r |
197 | if (mPhysMemAddressWidth > 48) {\r | |
198 | mPhysMemAddressWidth = 48;\r | |
3b96221f | 199 | }\r |
3b96221f AP |
200 | }\r |
201 | \r | |
3b96221f AP |
202 | /**\r |
203 | Calculate the cap for the permanent PEI memory.\r | |
204 | **/\r | |
205 | STATIC\r | |
206 | UINT32\r | |
207 | GetPeiMemoryCap (\r | |
208 | VOID\r | |
209 | )\r | |
210 | {\r | |
ac0a286f MK |
211 | BOOLEAN Page1GSupport;\r |
212 | UINT32 RegEax;\r | |
213 | UINT32 RegEdx;\r | |
214 | UINT32 Pml4Entries;\r | |
215 | UINT32 PdpEntries;\r | |
216 | UINTN TotalPages;\r | |
3b96221f AP |
217 | \r |
218 | //\r | |
219 | // If DXE is 32-bit, then just return the traditional 64 MB cap.\r | |
220 | //\r | |
ac0a286f | 221 | #ifdef MDE_CPU_IA32\r |
3b96221f AP |
222 | if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r |
223 | return SIZE_64MB;\r | |
224 | }\r | |
ac0a286f MK |
225 | \r |
226 | #endif\r | |
3b96221f AP |
227 | \r |
228 | //\r | |
229 | // Dependent on physical address width, PEI memory allocations can be\r | |
230 | // dominated by the page tables built for 64-bit DXE. So we key the cap off\r | |
231 | // of those. The code below is based on CreateIdentityMappingPageTables() in\r | |
232 | // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".\r | |
233 | //\r | |
234 | Page1GSupport = FALSE;\r | |
235 | if (PcdGetBool (PcdUse1GPageTable)) {\r | |
236 | AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r | |
237 | if (RegEax >= 0x80000001) {\r | |
238 | AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r | |
239 | if ((RegEdx & BIT26) != 0) {\r | |
240 | Page1GSupport = TRUE;\r | |
241 | }\r | |
242 | }\r | |
243 | }\r | |
244 | \r | |
245 | if (mPhysMemAddressWidth <= 39) {\r | |
246 | Pml4Entries = 1;\r | |
ac0a286f | 247 | PdpEntries = 1 << (mPhysMemAddressWidth - 30);\r |
3b96221f AP |
248 | ASSERT (PdpEntries <= 0x200);\r |
249 | } else {\r | |
250 | Pml4Entries = 1 << (mPhysMemAddressWidth - 39);\r | |
251 | ASSERT (Pml4Entries <= 0x200);\r | |
252 | PdpEntries = 512;\r | |
253 | }\r | |
254 | \r | |
255 | TotalPages = Page1GSupport ? Pml4Entries + 1 :\r | |
ac0a286f | 256 | (PdpEntries + 1) * Pml4Entries + 1;\r |
3b96221f AP |
257 | ASSERT (TotalPages <= 0x40201);\r |
258 | \r | |
259 | //\r | |
260 | // Add 64 MB for miscellaneous allocations. Note that for\r | |
261 | // mPhysMemAddressWidth values close to 36, the cap will actually be\r | |
262 | // dominated by this increment.\r | |
263 | //\r | |
264 | return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);\r | |
265 | }\r | |
266 | \r | |
3b96221f AP |
267 | /**\r |
268 | Publish PEI core memory\r | |
269 | \r | |
270 | @return EFI_SUCCESS The PEIM initialized successfully.\r | |
271 | \r | |
272 | **/\r | |
273 | EFI_STATUS\r | |
274 | PublishPeiMemory (\r | |
275 | VOID\r | |
276 | )\r | |
277 | {\r | |
ac0a286f MK |
278 | EFI_STATUS Status;\r |
279 | EFI_PHYSICAL_ADDRESS MemoryBase;\r | |
280 | UINT64 MemorySize;\r | |
281 | UINT32 LowerMemorySize;\r | |
282 | UINT32 PeiMemoryCap;\r | |
3b96221f AP |
283 | \r |
284 | LowerMemorySize = GetSystemMemorySizeBelow4gb ();\r | |
285 | \r | |
286 | if (mBootMode == BOOT_ON_S3_RESUME) {\r | |
287 | MemoryBase = mS3AcpiReservedMemoryBase;\r | |
288 | MemorySize = mS3AcpiReservedMemorySize;\r | |
289 | } else {\r | |
290 | PeiMemoryCap = GetPeiMemoryCap ();\r | |
ac0a286f MK |
291 | DEBUG ((\r |
292 | DEBUG_INFO,\r | |
293 | "%a: mPhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",\r | |
294 | __FUNCTION__,\r | |
295 | mPhysMemAddressWidth,\r | |
296 | PeiMemoryCap >> 10\r | |
297 | ));\r | |
3b96221f AP |
298 | \r |
299 | //\r | |
300 | // Determine the range of memory to use during PEI\r | |
301 | //\r | |
302 | MemoryBase =\r | |
303 | PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);\r | |
304 | MemorySize = LowerMemorySize - MemoryBase;\r | |
305 | if (MemorySize > PeiMemoryCap) {\r | |
306 | MemoryBase = LowerMemorySize - PeiMemoryCap;\r | |
307 | MemorySize = PeiMemoryCap;\r | |
308 | }\r | |
309 | }\r | |
310 | \r | |
311 | //\r | |
312 | // Publish this memory to the PEI Core\r | |
313 | //\r | |
ac0a286f | 314 | Status = PublishSystemMemory (MemoryBase, MemorySize);\r |
3b96221f AP |
315 | ASSERT_EFI_ERROR (Status);\r |
316 | \r | |
317 | return Status;\r | |
318 | }\r | |
319 | \r | |
3b96221f AP |
320 | /**\r |
321 | Publish system RAM and reserve memory regions\r | |
322 | \r | |
323 | **/\r | |
324 | VOID\r | |
325 | InitializeRamRegions (\r | |
326 | VOID\r | |
327 | )\r | |
328 | {\r | |
329 | XenPublishRamRegions ();\r | |
330 | \r | |
331 | if (mBootMode != BOOT_ON_S3_RESUME) {\r | |
332 | //\r | |
333 | // Reserve the lock box storage area\r | |
334 | //\r | |
335 | // Since this memory range will be used on S3 resume, it must be\r | |
336 | // reserved as ACPI NVS.\r | |
337 | //\r | |
338 | // If S3 is unsupported, then various drivers might still write to the\r | |
339 | // LockBox area. We ought to prevent DXE from serving allocation requests\r | |
340 | // such that they would overlap the LockBox storage.\r | |
341 | //\r | |
342 | ZeroMem (\r | |
ac0a286f MK |
343 | (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r |
344 | (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)\r | |
3b96221f AP |
345 | );\r |
346 | BuildMemoryAllocationHob (\r | |
ac0a286f MK |
347 | (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r |
348 | (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),\r | |
3b96221f AP |
349 | EfiBootServicesData\r |
350 | );\r | |
351 | }\r | |
352 | }\r |