]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformInitLib/MemDetect.c
OvmfPkg/Microvm/pcie: mPhysMemAddressWidth tweak
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / MemDetect.c
CommitLineData
10460942
MX
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 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7Module Name:\r
8\r
9 MemDetect.c\r
10\r
11**/\r
12\r
13//\r
14// The package level header files this module uses\r
15//\r
16#include <IndustryStandard/E820.h>\r
17#include <IndustryStandard/I440FxPiix4.h>\r
18#include <IndustryStandard/Q35MchIch9.h>\r
19#include <IndustryStandard/CloudHv.h>\r
20#include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>\r
21#include <PiPei.h>\r
22#include <Register/Intel/SmramSaveStateMap.h>\r
23\r
24//\r
25// The Library classes this module consumes\r
26//\r
27#include <Library/BaseLib.h>\r
28#include <Library/BaseMemoryLib.h>\r
29#include <Library/DebugLib.h>\r
30#include <Library/HobLib.h>\r
31#include <Library/IoLib.h>\r
32#include <Library/MemEncryptSevLib.h>\r
33#include <Library/PcdLib.h>\r
34#include <Library/PciLib.h>\r
35#include <Library/PeimEntryPoint.h>\r
36#include <Library/ResourcePublicationLib.h>\r
37#include <Library/MtrrLib.h>\r
38#include <Library/QemuFwCfgLib.h>\r
39#include <Library/QemuFwCfgSimpleParserLib.h>\r
e23f8f52
MX
40#include <Library/TdxLib.h>\r
41\r
10460942
MX
42#include <Library/PlatformInitLib.h>\r
43\r
44VOID\r
45EFIAPI\r
46PlatformQemuUc32BaseInitialization (\r
47 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
48 )\r
49{\r
50 UINT32 LowerMemorySize;\r
51\r
52 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
53 return;\r
54 }\r
55\r
56 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
57 //\r
58 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,\r
59 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for\r
60 // setting PcdPciExpressBaseAddress such that describing the\r
61 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of\r
62 // variable MTRRs (preferably 1 or 2).\r
63 //\r
47f44097
GH
64 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);\r
65 PlatformInfoHob->Uc32Base = (UINT32)PcdGet64 (PcdPciExpressBaseAddress);\r
10460942
MX
66 return;\r
67 }\r
68\r
69 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {\r
70 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;\r
71 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;\r
72 return;\r
73 }\r
74\r
75 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID);\r
76 //\r
77 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one\r
78 // variable MTRR suffices by truncating the size to a whole power of two,\r
79 // while keeping the end affixed to 4GB. This will round the base up.\r
80 //\r
81 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
82 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));\r
83 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);\r
84 //\r
85 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.\r
86 // Therefore Uc32Base is at least 2GB.\r
87 //\r
88 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);\r
89\r
90 if (PlatformInfoHob->Uc32Base != LowerMemorySize) {\r
91 DEBUG ((\r
92 DEBUG_VERBOSE,\r
93 "%a: rounded UC32 base from 0x%x up to 0x%x, for "\r
94 "an UC32 size of 0x%x\n",\r
95 __FUNCTION__,\r
96 LowerMemorySize,\r
97 PlatformInfoHob->Uc32Base,\r
98 PlatformInfoHob->Uc32Size\r
99 ));\r
100 }\r
101}\r
102\r
103/**\r
104 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside\r
105 of the 32-bit address range.\r
106\r
107 Find the highest exclusive >=4GB RAM address, or produce memory resource\r
108 descriptor HOBs for RAM entries that start at or above 4GB.\r
109\r
110 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()\r
111 produces memory resource descriptor HOBs for RAM\r
112 entries that start at or above 4GB.\r
113\r
114 Otherwise, MaxAddress holds the highest exclusive\r
115 >=4GB RAM address on output. If QEMU's fw_cfg E820\r
116 RAM map contains no RAM entry that starts outside of\r
117 the 32-bit address range, then MaxAddress is exactly\r
118 4GB on output.\r
119\r
120 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.\r
121\r
122 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a\r
123 whole multiple of sizeof(EFI_E820_ENTRY64). No\r
124 RAM entry was processed.\r
125\r
126 @return Error codes from QemuFwCfgFindFile(). No RAM\r
127 entry was processed.\r
128**/\r
129STATIC\r
130EFI_STATUS\r
131PlatformScanOrAdd64BitE820Ram (\r
132 IN BOOLEAN AddHighHob,\r
133 OUT UINT64 *LowMemory OPTIONAL,\r
134 OUT UINT64 *MaxAddress OPTIONAL\r
135 )\r
136{\r
137 EFI_STATUS Status;\r
138 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
139 UINTN FwCfgSize;\r
140 EFI_E820_ENTRY64 E820Entry;\r
141 UINTN Processed;\r
142\r
143 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);\r
144 if (EFI_ERROR (Status)) {\r
145 return Status;\r
146 }\r
147\r
148 if (FwCfgSize % sizeof E820Entry != 0) {\r
149 return EFI_PROTOCOL_ERROR;\r
150 }\r
151\r
152 if (LowMemory != NULL) {\r
153 *LowMemory = 0;\r
154 }\r
155\r
156 if (MaxAddress != NULL) {\r
157 *MaxAddress = BASE_4GB;\r
158 }\r
159\r
160 QemuFwCfgSelectItem (FwCfgItem);\r
161 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {\r
162 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);\r
163 DEBUG ((\r
164 DEBUG_VERBOSE,\r
165 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",\r
166 __FUNCTION__,\r
167 E820Entry.BaseAddr,\r
168 E820Entry.Length,\r
169 E820Entry.Type\r
170 ));\r
171 if (E820Entry.Type == EfiAcpiAddressRangeMemory) {\r
172 if (AddHighHob && (E820Entry.BaseAddr >= BASE_4GB)) {\r
173 UINT64 Base;\r
174 UINT64 End;\r
175\r
176 //\r
177 // Round up the start address, and round down the end address.\r
178 //\r
179 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
180 End = (E820Entry.BaseAddr + E820Entry.Length) &\r
181 ~(UINT64)EFI_PAGE_MASK;\r
182 if (Base < End) {\r
183 PlatformAddMemoryRangeHob (Base, End);\r
184 DEBUG ((\r
185 DEBUG_VERBOSE,\r
186 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",\r
187 __FUNCTION__,\r
188 Base,\r
189 End\r
190 ));\r
191 }\r
192 }\r
193\r
194 if (MaxAddress || LowMemory) {\r
195 UINT64 Candidate;\r
196\r
197 Candidate = E820Entry.BaseAddr + E820Entry.Length;\r
198 if (MaxAddress && (Candidate > *MaxAddress)) {\r
199 *MaxAddress = Candidate;\r
200 DEBUG ((\r
201 DEBUG_VERBOSE,\r
202 "%a: MaxAddress=0x%Lx\n",\r
203 __FUNCTION__,\r
204 *MaxAddress\r
205 ));\r
206 }\r
207\r
208 if (LowMemory && (Candidate > *LowMemory) && (Candidate < BASE_4GB)) {\r
209 *LowMemory = Candidate;\r
210 DEBUG ((\r
211 DEBUG_VERBOSE,\r
212 "%a: LowMemory=0x%Lx\n",\r
213 __FUNCTION__,\r
214 *LowMemory\r
215 ));\r
216 }\r
217 }\r
218 }\r
219 }\r
220\r
221 return EFI_SUCCESS;\r
222}\r
223\r
224/**\r
225 Returns PVH memmap\r
226\r
227 @param Entries Pointer to PVH memmap\r
228 @param Count Number of entries\r
229\r
230 @return EFI_STATUS\r
231**/\r
232EFI_STATUS\r
233GetPvhMemmapEntries (\r
234 struct hvm_memmap_table_entry **Entries,\r
235 UINT32 *Count\r
236 )\r
237{\r
238 UINT32 *PVHResetVectorData;\r
239 struct hvm_start_info *pvh_start_info;\r
240\r
241 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
242 if (PVHResetVectorData == 0) {\r
243 return EFI_NOT_FOUND;\r
244 }\r
245\r
246 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];\r
247\r
248 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;\r
249 *Count = pvh_start_info->memmap_entries;\r
250\r
251 return EFI_SUCCESS;\r
252}\r
253\r
254STATIC\r
255UINT64\r
256GetHighestSystemMemoryAddressFromPvhMemmap (\r
257 BOOLEAN Below4gb\r
258 )\r
259{\r
260 struct hvm_memmap_table_entry *Memmap;\r
261 UINT32 MemmapEntriesCount;\r
262 struct hvm_memmap_table_entry *Entry;\r
263 EFI_STATUS Status;\r
264 UINT32 Loop;\r
265 UINT64 HighestAddress;\r
266 UINT64 EntryEnd;\r
267\r
268 HighestAddress = 0;\r
269\r
270 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);\r
271 ASSERT_EFI_ERROR (Status);\r
272\r
273 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {\r
274 Entry = Memmap + Loop;\r
275 EntryEnd = Entry->addr + Entry->size;\r
276\r
277 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&\r
278 (EntryEnd > HighestAddress))\r
279 {\r
280 if (Below4gb && (EntryEnd <= BASE_4GB)) {\r
281 HighestAddress = EntryEnd;\r
282 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {\r
283 HighestAddress = EntryEnd;\r
284 }\r
285 }\r
286 }\r
287\r
288 return HighestAddress;\r
289}\r
290\r
291UINT32\r
292EFIAPI\r
293PlatformGetSystemMemorySizeBelow4gb (\r
294 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
295 )\r
296{\r
297 EFI_STATUS Status;\r
298 UINT64 LowerMemorySize = 0;\r
299 UINT8 Cmos0x34;\r
300 UINT8 Cmos0x35;\r
301\r
302 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {\r
303 // Get the information from PVH memmap\r
304 return (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);\r
305 }\r
306\r
307 Status = PlatformScanOrAdd64BitE820Ram (FALSE, &LowerMemorySize, NULL);\r
308 if ((Status == EFI_SUCCESS) && (LowerMemorySize > 0)) {\r
309 return (UINT32)LowerMemorySize;\r
310 }\r
311\r
312 //\r
313 // CMOS 0x34/0x35 specifies the system memory above 16 MB.\r
314 // * CMOS(0x35) is the high byte\r
315 // * CMOS(0x34) is the low byte\r
316 // * The size is specified in 64kb chunks\r
317 // * Since this is memory above 16MB, the 16MB must be added\r
318 // into the calculation to get the total memory size.\r
319 //\r
320\r
321 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);\r
322 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);\r
323\r
324 return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
325}\r
326\r
327STATIC\r
328UINT64\r
329PlatformGetSystemMemorySizeAbove4gb (\r
330 )\r
331{\r
332 UINT32 Size;\r
333 UINTN CmosIndex;\r
334\r
335 //\r
336 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.\r
337 // * CMOS(0x5d) is the most significant size byte\r
338 // * CMOS(0x5c) is the middle size byte\r
339 // * CMOS(0x5b) is the least significant size byte\r
340 // * The size is specified in 64kb chunks\r
341 //\r
342\r
343 Size = 0;\r
344 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {\r
345 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);\r
346 }\r
347\r
348 return LShiftU64 (Size, 16);\r
349}\r
350\r
351/**\r
352 Return the highest address that DXE could possibly use, plus one.\r
353**/\r
354STATIC\r
355UINT64\r
356PlatformGetFirstNonAddress (\r
357 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
358 )\r
359{\r
360 UINT64 FirstNonAddress;\r
361 UINT32 FwCfgPciMmio64Mb;\r
362 EFI_STATUS Status;\r
363 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
364 UINTN FwCfgSize;\r
365 UINT64 HotPlugMemoryEnd;\r
366\r
367 //\r
368 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings\r
369 //\r
370 FirstNonAddress = 0;\r
371\r
372 //\r
373 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM\r
374 // address from it. This can express an address >= 4GB+1TB.\r
375 //\r
376 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which\r
377 // can only express a size smaller than 1TB), and add it to 4GB.\r
378 //\r
379 Status = PlatformScanOrAdd64BitE820Ram (FALSE, NULL, &FirstNonAddress);\r
380 if (EFI_ERROR (Status)) {\r
381 FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();\r
382 }\r
383\r
384 //\r
385 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r
386 // resources to 32-bit anyway. See DegradeResource() in\r
387 // "PciResourceSupport.c".\r
388 //\r
389 #ifdef MDE_CPU_IA32\r
390 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
391 return FirstNonAddress;\r
392 }\r
393\r
394 #endif\r
395\r
396 //\r
397 // See if the user specified the number of megabytes for the 64-bit PCI host\r
398 // aperture. Accept an aperture size up to 16TB.\r
399 //\r
400 // As signaled by the "X-" prefix, this knob is experimental, and might go\r
401 // away at any time.\r
402 //\r
403 Status = QemuFwCfgParseUint32 (\r
404 "opt/ovmf/X-PciMmio64Mb",\r
405 FALSE,\r
406 &FwCfgPciMmio64Mb\r
407 );\r
408 switch (Status) {\r
409 case EFI_UNSUPPORTED:\r
410 case EFI_NOT_FOUND:\r
411 break;\r
412 case EFI_SUCCESS:\r
413 if (FwCfgPciMmio64Mb <= 0x1000000) {\r
414 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);\r
415 break;\r
416 }\r
417\r
418 //\r
419 // fall through\r
420 //\r
421 default:\r
422 DEBUG ((\r
423 DEBUG_WARN,\r
424 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",\r
425 __FUNCTION__\r
426 ));\r
427 break;\r
428 }\r
429\r
430 if (PlatformInfoHob->PcdPciMmio64Size == 0) {\r
431 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r
432 DEBUG ((\r
433 DEBUG_INFO,\r
434 "%a: disabling 64-bit PCI host aperture\n",\r
435 __FUNCTION__\r
436 ));\r
437 }\r
438\r
439 //\r
440 // There's nothing more to do; the amount of memory above 4GB fully\r
441 // determines the highest address plus one. The memory hotplug area (see\r
442 // below) plays no role for the firmware in this case.\r
443 //\r
444 return FirstNonAddress;\r
445 }\r
446\r
447 //\r
448 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an\r
449 // absolute, exclusive end address for the memory hotplug area. This area\r
450 // starts right at the end of the memory above 4GB. The 64-bit PCI host\r
451 // aperture must be placed above it.\r
452 //\r
453 Status = QemuFwCfgFindFile (\r
454 "etc/reserved-memory-end",\r
455 &FwCfgItem,\r
456 &FwCfgSize\r
457 );\r
458 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {\r
459 QemuFwCfgSelectItem (FwCfgItem);\r
460 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);\r
461 DEBUG ((\r
462 DEBUG_VERBOSE,\r
463 "%a: HotPlugMemoryEnd=0x%Lx\n",\r
464 __FUNCTION__,\r
465 HotPlugMemoryEnd\r
466 ));\r
467\r
468 ASSERT (HotPlugMemoryEnd >= FirstNonAddress);\r
469 FirstNonAddress = HotPlugMemoryEnd;\r
470 }\r
471\r
472 //\r
473 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so\r
474 // that the host can map it with 1GB hugepages. Follow suit.\r
475 //\r
476 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);\r
477 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);\r
478\r
479 //\r
480 // The 64-bit PCI host aperture should also be "naturally" aligned. The\r
481 // alignment is determined by rounding the size of the aperture down to the\r
482 // next smaller or equal power of two. That is, align the aperture by the\r
483 // largest BAR size that can fit into it.\r
484 //\r
485 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));\r
486\r
487 //\r
488 // The useful address space ends with the 64-bit PCI host aperture.\r
489 //\r
490 FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;\r
491 return FirstNonAddress;\r
492}\r
493\r
bd10d4e2
GH
494/*\r
495 * Use CPUID to figure physical address width. Does *not* work\r
496 * reliable on qemu. For historical reasons qemu returns phys-bits=40\r
497 * even in case the host machine supports less than that.\r
498 *\r
499 * qemu has a cpu property (host-phys-bits={on,off}) to change that\r
500 * and make sure guest phys-bits are not larger than host phys-bits.,\r
501 * but it is off by default. Exception: microvm machine type\r
502 * hard-wires that property to on.\r
503 */\r
504VOID\r
505EFIAPI\r
506PlatformAddressWidthFromCpuid (\r
507 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
508 )\r
509{\r
510 UINT32 RegEax;\r
511\r
512 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
513 if (RegEax >= 0x80000008) {\r
514 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
515 PlatformInfoHob->PhysMemAddressWidth = (UINT8)RegEax;\r
516 } else {\r
517 PlatformInfoHob->PhysMemAddressWidth = 36;\r
518 }\r
519\r
520 PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);\r
521\r
522 DEBUG ((\r
523 DEBUG_INFO,\r
524 "%a: cpuid: phys-bits is %d\n",\r
525 __FUNCTION__,\r
526 PlatformInfoHob->PhysMemAddressWidth\r
527 ));\r
528}\r
529\r
10460942
MX
530/**\r
531 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.\r
532**/\r
533VOID\r
534EFIAPI\r
535PlatformAddressWidthInitialization (\r
536 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
537 )\r
538{\r
539 UINT64 FirstNonAddress;\r
540 UINT8 PhysMemAddressWidth;\r
541\r
bd10d4e2
GH
542 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
543 PlatformAddressWidthFromCpuid (PlatformInfoHob);\r
544 return;\r
545 }\r
546\r
10460942
MX
547 //\r
548 // As guest-physical memory size grows, the permanent PEI RAM requirements\r
549 // are dominated by the identity-mapping page tables built by the DXE IPL.\r
550 // The DXL IPL keys off of the physical address bits advertized in the CPU\r
551 // HOB. To conserve memory, we calculate the minimum address width here.\r
552 //\r
553 FirstNonAddress = PlatformGetFirstNonAddress (PlatformInfoHob);\r
554 PhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);\r
555\r
556 //\r
557 // If FirstNonAddress is not an integral power of two, then we need an\r
558 // additional bit.\r
559 //\r
560 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {\r
561 ++PhysMemAddressWidth;\r
562 }\r
563\r
564 //\r
565 // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
566 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
567 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
568 // can simply assert that here, since 48 bits are good enough for 256 TB.\r
569 //\r
570 if (PhysMemAddressWidth <= 36) {\r
571 PhysMemAddressWidth = 36;\r
572 }\r
573\r
e23f8f52
MX
574 #if defined (MDE_CPU_X64)\r
575 if (TdIsEnabled ()) {\r
576 if (TdSharedPageMask () == (1ULL << 47)) {\r
577 PhysMemAddressWidth = 48;\r
578 } else {\r
579 PhysMemAddressWidth = 52;\r
580 }\r
581 }\r
582\r
583 ASSERT (PhysMemAddressWidth <= 52);\r
584 #else\r
10460942 585 ASSERT (PhysMemAddressWidth <= 48);\r
e23f8f52 586 #endif\r
10460942
MX
587\r
588 PlatformInfoHob->FirstNonAddress = FirstNonAddress;\r
589 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;\r
590}\r
591\r
592STATIC\r
593VOID\r
594QemuInitializeRamBelow1gb (\r
595 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
596 )\r
597{\r
598 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {\r
599 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);\r
600 PlatformAddReservedMemoryBaseSizeHob (\r
601 SMM_DEFAULT_SMBASE,\r
602 MCH_DEFAULT_SMBASE_SIZE,\r
603 TRUE /* Cacheable */\r
604 );\r
605 STATIC_ASSERT (\r
606 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,\r
607 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"\r
608 );\r
609 PlatformAddMemoryRangeHob (\r
610 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,\r
611 BASE_512KB + BASE_128KB\r
612 );\r
613 } else {\r
614 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
615 }\r
616}\r
617\r
618/**\r
619 Peform Memory Detection for QEMU / KVM\r
620\r
621**/\r
622VOID\r
623EFIAPI\r
624PlatformQemuInitializeRam (\r
625 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
626 )\r
627{\r
628 UINT64 LowerMemorySize;\r
629 UINT64 UpperMemorySize;\r
630 MTRR_SETTINGS MtrrSettings;\r
631 EFI_STATUS Status;\r
632\r
633 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));\r
634\r
635 //\r
636 // Determine total memory size available\r
637 //\r
638 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
639\r
640 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {\r
641 //\r
642 // Create the following memory HOB as an exception on the S3 boot path.\r
643 //\r
644 // Normally we'd create memory HOBs only on the normal boot path. However,\r
645 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as\r
646 // well, for "borrowing" a subset of it temporarily, for the AP startup\r
647 // vector.\r
648 //\r
649 // CpuMpPei saves the original contents of the borrowed area in permanent\r
650 // PEI RAM, in a backup buffer allocated with the normal PEI services.\r
651 // CpuMpPei restores the original contents ("returns" the borrowed area) at\r
652 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before\r
653 // transferring control to the OS's wakeup vector in the FACS.\r
654 //\r
655 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to\r
656 // restore the original contents. Furthermore, we expect all such PEIMs\r
657 // (CpuMpPei included) to claim the borrowed areas by producing memory\r
658 // allocation HOBs, and to honor preexistent memory allocation HOBs when\r
659 // looking for an area to borrow.\r
660 //\r
661 QemuInitializeRamBelow1gb (PlatformInfoHob);\r
662 } else {\r
663 //\r
664 // Create memory HOBs\r
665 //\r
666 QemuInitializeRamBelow1gb (PlatformInfoHob);\r
667\r
668 if (PlatformInfoHob->SmmSmramRequire) {\r
669 UINT32 TsegSize;\r
670\r
671 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;\r
672 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);\r
673 PlatformAddReservedMemoryBaseSizeHob (\r
674 LowerMemorySize - TsegSize,\r
675 TsegSize,\r
676 TRUE\r
677 );\r
678 } else {\r
679 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize);\r
680 }\r
681\r
682 //\r
683 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM\r
684 // entries. Otherwise, create a single memory HOB with the flat >=4GB\r
685 // memory size read from the CMOS.\r
686 //\r
687 Status = PlatformScanOrAdd64BitE820Ram (TRUE, NULL, NULL);\r
688 if (EFI_ERROR (Status)) {\r
689 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();\r
690 if (UpperMemorySize != 0) {\r
691 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
692 }\r
693 }\r
694 }\r
695\r
696 //\r
697 // We'd like to keep the following ranges uncached:\r
698 // - [640 KB, 1 MB)\r
699 // - [LowerMemorySize, 4 GB)\r
700 //\r
701 // Everything else should be WB. Unfortunately, programming the inverse (ie.\r
702 // keeping the default UC, and configuring the complement set of the above as\r
703 // WB) is not reliable in general, because the end of the upper RAM can have\r
704 // practically any alignment, and we may not have enough variable MTRRs to\r
705 // cover it exactly.\r
706 //\r
707 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {\r
708 MtrrGetAllMtrrs (&MtrrSettings);\r
709\r
710 //\r
711 // MTRRs disabled, fixed MTRRs disabled, default type is uncached\r
712 //\r
713 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);\r
714 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);\r
715 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);\r
716\r
717 //\r
718 // flip default type to writeback\r
719 //\r
720 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);\r
721 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);\r
722 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;\r
723 MtrrSetAllMtrrs (&MtrrSettings);\r
724\r
725 //\r
726 // Set memory range from 640KB to 1MB to uncacheable\r
727 //\r
728 Status = MtrrSetMemoryAttribute (\r
729 BASE_512KB + BASE_128KB,\r
730 BASE_1MB - (BASE_512KB + BASE_128KB),\r
731 CacheUncacheable\r
732 );\r
733 ASSERT_EFI_ERROR (Status);\r
734\r
735 //\r
736 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI\r
737 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.\r
738 //\r
739 Status = MtrrSetMemoryAttribute (\r
740 PlatformInfoHob->Uc32Base,\r
741 SIZE_4GB - PlatformInfoHob->Uc32Base,\r
742 CacheUncacheable\r
743 );\r
744 ASSERT_EFI_ERROR (Status);\r
745 }\r
746}\r
747\r
748VOID\r
749EFIAPI\r
750PlatformQemuInitializeRamForS3 (\r
751 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
752 )\r
753{\r
754 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {\r
755 //\r
756 // This is the memory range that will be used for PEI on S3 resume\r
757 //\r
758 BuildMemoryAllocationHob (\r
759 PlatformInfoHob->S3AcpiReservedMemoryBase,\r
760 PlatformInfoHob->S3AcpiReservedMemorySize,\r
761 EfiACPIMemoryNVS\r
762 );\r
763\r
764 //\r
765 // Cover the initial RAM area used as stack and temporary PEI heap.\r
766 //\r
767 // This is reserved as ACPI NVS so it can be used on S3 resume.\r
768 //\r
769 BuildMemoryAllocationHob (\r
770 PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
771 PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
772 EfiACPIMemoryNVS\r
773 );\r
774\r
775 //\r
776 // SEC stores its table of GUIDed section handlers here.\r
777 //\r
778 BuildMemoryAllocationHob (\r
779 PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
780 PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
781 EfiACPIMemoryNVS\r
782 );\r
783\r
784 #ifdef MDE_CPU_X64\r
785 //\r
786 // Reserve the initial page tables built by the reset vector code.\r
787 //\r
788 // Since this memory range will be used by the Reset Vector on S3\r
789 // resume, it must be reserved as ACPI NVS.\r
790 //\r
791 BuildMemoryAllocationHob (\r
792 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),\r
793 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),\r
794 EfiACPIMemoryNVS\r
795 );\r
796\r
797 if (PlatformInfoHob->SevEsIsEnabled) {\r
798 //\r
799 // If SEV-ES is enabled, reserve the GHCB-related memory area. This\r
800 // includes the extra page table used to break down the 2MB page\r
801 // mapping into 4KB page entries where the GHCB resides and the\r
802 // GHCB area itself.\r
803 //\r
804 // Since this memory range will be used by the Reset Vector on S3\r
805 // resume, it must be reserved as ACPI NVS.\r
806 //\r
807 BuildMemoryAllocationHob (\r
808 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),\r
809 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),\r
810 EfiACPIMemoryNVS\r
811 );\r
812 BuildMemoryAllocationHob (\r
813 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),\r
814 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),\r
815 EfiACPIMemoryNVS\r
816 );\r
817 BuildMemoryAllocationHob (\r
818 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),\r
819 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),\r
820 EfiACPIMemoryNVS\r
821 );\r
822 }\r
823\r
824 #endif\r
825 }\r
826\r
827 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r
828 if (!PlatformInfoHob->SmmSmramRequire) {\r
829 //\r
830 // Reserve the lock box storage area\r
831 //\r
832 // Since this memory range will be used on S3 resume, it must be\r
833 // reserved as ACPI NVS.\r
834 //\r
835 // If S3 is unsupported, then various drivers might still write to the\r
836 // LockBox area. We ought to prevent DXE from serving allocation requests\r
837 // such that they would overlap the LockBox storage.\r
838 //\r
839 ZeroMem (\r
840 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r
841 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)\r
842 );\r
843 BuildMemoryAllocationHob (\r
844 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r
845 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),\r
846 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
847 );\r
848 }\r
849\r
850 if (PlatformInfoHob->SmmSmramRequire) {\r
851 UINT32 TsegSize;\r
852\r
853 //\r
854 // Make sure the TSEG area that we reported as a reserved memory resource\r
855 // cannot be used for reserved memory allocations.\r
856 //\r
857 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;\r
858 BuildMemoryAllocationHob (\r
859 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob) - TsegSize,\r
860 TsegSize,\r
861 EfiReservedMemoryType\r
862 );\r
863 //\r
864 // Similarly, allocate away the (already reserved) SMRAM at the default\r
865 // SMBASE, if it exists.\r
866 //\r
867 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {\r
868 BuildMemoryAllocationHob (\r
869 SMM_DEFAULT_SMBASE,\r
870 MCH_DEFAULT_SMBASE_SIZE,\r
871 EfiReservedMemoryType\r
872 );\r
873 }\r
874 }\r
875\r
876 #ifdef MDE_CPU_X64\r
877 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {\r
878 //\r
879 // Reserve the work area.\r
880 //\r
881 // Since this memory range will be used by the Reset Vector on S3\r
882 // resume, it must be reserved as ACPI NVS.\r
883 //\r
884 // If S3 is unsupported, then various drivers might still write to the\r
885 // work area. We ought to prevent DXE from serving allocation requests\r
886 // such that they would overlap the work area.\r
887 //\r
888 BuildMemoryAllocationHob (\r
889 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),\r
890 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),\r
891 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
892 );\r
893 }\r
894\r
895 #endif\r
896 }\r
897}\r