]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformInitLib/MemDetect.c
OvmfPkg/PlatformInitLib: Add PlatformGetLowMemoryCB
[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
e03b0d06 29#include <Library/CcProbeLib.h>\r
10460942 30#include <Library/DebugLib.h>\r
3497fd5c 31#include <Library/HardwareInfoLib.h>\r
10460942
MX
32#include <Library/HobLib.h>\r
33#include <Library/IoLib.h>\r
34#include <Library/MemEncryptSevLib.h>\r
35#include <Library/PcdLib.h>\r
36#include <Library/PciLib.h>\r
37#include <Library/PeimEntryPoint.h>\r
38#include <Library/ResourcePublicationLib.h>\r
39#include <Library/MtrrLib.h>\r
40#include <Library/QemuFwCfgLib.h>\r
41#include <Library/QemuFwCfgSimpleParserLib.h>\r
e23f8f52
MX
42#include <Library/TdxLib.h>\r
43\r
10460942
MX
44#include <Library/PlatformInitLib.h>\r
45\r
d1e41c62
MX
46#define MEGABYTE_SHIFT 20\r
47\r
10460942
MX
48VOID\r
49EFIAPI\r
50PlatformQemuUc32BaseInitialization (\r
51 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
52 )\r
53{\r
10460942
MX
54 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
55 return;\r
56 }\r
57\r
58 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
124b7650 59 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
47f44097 60 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);\r
124b7650 61 ASSERT (PcdGet64 (PcdPciExpressBaseAddress) >= PlatformInfoHob->LowMemory);\r
2a0bd3bf 62\r
124b7650 63 if (PlatformInfoHob->LowMemory <= BASE_2GB) {\r
2a0bd3bf
GH
64 // Newer qemu with gigabyte aligned memory,\r
65 // 32-bit pci mmio window is 2G -> 4G then.\r
66 PlatformInfoHob->Uc32Base = BASE_2GB;\r
67 } else {\r
68 //\r
69 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,\r
70 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for\r
71 // setting PcdPciExpressBaseAddress such that describing the\r
72 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of\r
73 // variable MTRRs (preferably 1 or 2).\r
74 //\r
75 PlatformInfoHob->Uc32Base = (UINT32)PcdGet64 (PcdPciExpressBaseAddress);\r
76 }\r
77\r
10460942
MX
78 return;\r
79 }\r
80\r
81 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {\r
82 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;\r
83 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;\r
84 return;\r
85 }\r
86\r
87 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID);\r
88 //\r
89 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one\r
90 // variable MTRR suffices by truncating the size to a whole power of two,\r
91 // while keeping the end affixed to 4GB. This will round the base up.\r
92 //\r
124b7650
GH
93 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
94 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - PlatformInfoHob->LowMemory));\r
10460942
MX
95 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);\r
96 //\r
97 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.\r
98 // Therefore Uc32Base is at least 2GB.\r
99 //\r
100 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);\r
101\r
124b7650 102 if (PlatformInfoHob->Uc32Base != PlatformInfoHob->LowMemory) {\r
10460942
MX
103 DEBUG ((\r
104 DEBUG_VERBOSE,\r
105 "%a: rounded UC32 base from 0x%x up to 0x%x, for "\r
106 "an UC32 size of 0x%x\n",\r
107 __FUNCTION__,\r
124b7650 108 PlatformInfoHob->LowMemory,\r
10460942
MX
109 PlatformInfoHob->Uc32Base,\r
110 PlatformInfoHob->Uc32Size\r
111 ));\r
112 }\r
113}\r
114\r
115/**\r
116 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside\r
117 of the 32-bit address range.\r
118\r
119 Find the highest exclusive >=4GB RAM address, or produce memory resource\r
120 descriptor HOBs for RAM entries that start at or above 4GB.\r
121\r
122 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()\r
123 produces memory resource descriptor HOBs for RAM\r
124 entries that start at or above 4GB.\r
125\r
126 Otherwise, MaxAddress holds the highest exclusive\r
127 >=4GB RAM address on output. If QEMU's fw_cfg E820\r
128 RAM map contains no RAM entry that starts outside of\r
129 the 32-bit address range, then MaxAddress is exactly\r
130 4GB on output.\r
131\r
132 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.\r
133\r
134 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a\r
135 whole multiple of sizeof(EFI_E820_ENTRY64). No\r
136 RAM entry was processed.\r
137\r
138 @return Error codes from QemuFwCfgFindFile(). No RAM\r
139 entry was processed.\r
140**/\r
141STATIC\r
142EFI_STATUS\r
143PlatformScanOrAdd64BitE820Ram (\r
144 IN BOOLEAN AddHighHob,\r
145 OUT UINT64 *LowMemory OPTIONAL,\r
146 OUT UINT64 *MaxAddress OPTIONAL\r
147 )\r
148{\r
149 EFI_STATUS Status;\r
150 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
151 UINTN FwCfgSize;\r
152 EFI_E820_ENTRY64 E820Entry;\r
153 UINTN Processed;\r
154\r
155 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);\r
156 if (EFI_ERROR (Status)) {\r
157 return Status;\r
158 }\r
159\r
160 if (FwCfgSize % sizeof E820Entry != 0) {\r
161 return EFI_PROTOCOL_ERROR;\r
162 }\r
163\r
164 if (LowMemory != NULL) {\r
165 *LowMemory = 0;\r
166 }\r
167\r
168 if (MaxAddress != NULL) {\r
169 *MaxAddress = BASE_4GB;\r
170 }\r
171\r
172 QemuFwCfgSelectItem (FwCfgItem);\r
173 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {\r
174 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);\r
175 DEBUG ((\r
176 DEBUG_VERBOSE,\r
177 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",\r
178 __FUNCTION__,\r
179 E820Entry.BaseAddr,\r
180 E820Entry.Length,\r
181 E820Entry.Type\r
182 ));\r
183 if (E820Entry.Type == EfiAcpiAddressRangeMemory) {\r
184 if (AddHighHob && (E820Entry.BaseAddr >= BASE_4GB)) {\r
185 UINT64 Base;\r
186 UINT64 End;\r
187\r
188 //\r
189 // Round up the start address, and round down the end address.\r
190 //\r
191 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);\r
192 End = (E820Entry.BaseAddr + E820Entry.Length) &\r
193 ~(UINT64)EFI_PAGE_MASK;\r
194 if (Base < End) {\r
195 PlatformAddMemoryRangeHob (Base, End);\r
196 DEBUG ((\r
197 DEBUG_VERBOSE,\r
198 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",\r
199 __FUNCTION__,\r
200 Base,\r
201 End\r
202 ));\r
203 }\r
204 }\r
205\r
206 if (MaxAddress || LowMemory) {\r
207 UINT64 Candidate;\r
208\r
209 Candidate = E820Entry.BaseAddr + E820Entry.Length;\r
210 if (MaxAddress && (Candidate > *MaxAddress)) {\r
211 *MaxAddress = Candidate;\r
212 DEBUG ((\r
213 DEBUG_VERBOSE,\r
214 "%a: MaxAddress=0x%Lx\n",\r
215 __FUNCTION__,\r
216 *MaxAddress\r
217 ));\r
218 }\r
219\r
220 if (LowMemory && (Candidate > *LowMemory) && (Candidate < BASE_4GB)) {\r
221 *LowMemory = Candidate;\r
222 DEBUG ((\r
223 DEBUG_VERBOSE,\r
224 "%a: LowMemory=0x%Lx\n",\r
225 __FUNCTION__,\r
226 *LowMemory\r
227 ));\r
228 }\r
229 }\r
bf65d7ee
GH
230 } else if (E820Entry.Type == EfiAcpiAddressRangeReserved) {\r
231 if (AddHighHob) {\r
232 DEBUG ((\r
233 DEBUG_INFO,\r
234 "%a: Reserved: Base=0x%Lx Length=0x%Lx\n",\r
235 __FUNCTION__,\r
236 E820Entry.BaseAddr,\r
237 E820Entry.Length\r
238 ));\r
239 BuildResourceDescriptorHob (\r
240 EFI_RESOURCE_MEMORY_RESERVED,\r
241 0,\r
242 E820Entry.BaseAddr,\r
243 E820Entry.Length\r
244 );\r
245 }\r
10460942
MX
246 }\r
247 }\r
248\r
249 return EFI_SUCCESS;\r
250}\r
251\r
e0375304
GH
252typedef VOID (*E820_SCAN_CALLBACK) (\r
253 EFI_E820_ENTRY64 *E820Entry,\r
254 EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
255 );\r
256\r
257/**\r
258 Store first address not used by e820 RAM entries in\r
259 PlatformInfoHob->FirstNonAddress\r
260**/\r
261STATIC\r
262VOID\r
263PlatformGetFirstNonAddressCB (\r
264 IN EFI_E820_ENTRY64 *E820Entry,\r
265 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
266 )\r
267{\r
268 UINT64 Candidate;\r
269\r
270 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {\r
271 return;\r
272 }\r
273\r
274 Candidate = E820Entry->BaseAddr + E820Entry->Length;\r
275 if (PlatformInfoHob->FirstNonAddress < Candidate) {\r
276 DEBUG ((DEBUG_INFO, "%a: FirstNonAddress=0x%Lx\n", __FUNCTION__, Candidate));\r
277 PlatformInfoHob->FirstNonAddress = Candidate;\r
278 }\r
279}\r
280\r
124b7650
GH
281/**\r
282 Store the low (below 4G) memory size in\r
283 PlatformInfoHob->LowMemory\r
284**/\r
285STATIC\r
286VOID\r
287PlatformGetLowMemoryCB (\r
288 IN EFI_E820_ENTRY64 *E820Entry,\r
289 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
290 )\r
291{\r
292 UINT64 Candidate;\r
293\r
294 if (E820Entry->Type != EfiAcpiAddressRangeMemory) {\r
295 return;\r
296 }\r
297\r
298 Candidate = E820Entry->BaseAddr + E820Entry->Length;\r
299 if (Candidate >= BASE_4GB) {\r
300 return;\r
301 }\r
302\r
303 if (PlatformInfoHob->LowMemory < Candidate) {\r
304 DEBUG ((DEBUG_INFO, "%a: LowMemory=0x%Lx\n", __FUNCTION__, Candidate));\r
305 PlatformInfoHob->LowMemory = (UINT32)Candidate;\r
306 }\r
307}\r
308\r
e0375304
GH
309/**\r
310 Iterate over the entries in QEMU's fw_cfg E820 RAM map, call the\r
311 passed callback for each entry.\r
312\r
313 @param[in] Callback The callback function to be called.\r
314\r
315 @param[in out] PlatformInfoHob PlatformInfo struct which is passed\r
316 through to the callback.\r
317\r
318 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.\r
319\r
320 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a\r
321 whole multiple of sizeof(EFI_E820_ENTRY64). No\r
322 RAM entry was processed.\r
323\r
324 @return Error codes from QemuFwCfgFindFile(). No RAM\r
325 entry was processed.\r
326**/\r
327STATIC\r
328EFI_STATUS\r
329PlatformScanE820 (\r
330 IN E820_SCAN_CALLBACK Callback,\r
331 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
332 )\r
333{\r
334 EFI_STATUS Status;\r
335 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
336 UINTN FwCfgSize;\r
337 EFI_E820_ENTRY64 E820Entry;\r
338 UINTN Processed;\r
339\r
340 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);\r
341 if (EFI_ERROR (Status)) {\r
342 return Status;\r
343 }\r
344\r
345 if (FwCfgSize % sizeof E820Entry != 0) {\r
346 return EFI_PROTOCOL_ERROR;\r
347 }\r
348\r
349 QemuFwCfgSelectItem (FwCfgItem);\r
350 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {\r
351 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);\r
352 Callback (&E820Entry, PlatformInfoHob);\r
353 }\r
354\r
355 return EFI_SUCCESS;\r
356}\r
357\r
10460942
MX
358/**\r
359 Returns PVH memmap\r
360\r
361 @param Entries Pointer to PVH memmap\r
362 @param Count Number of entries\r
363\r
364 @return EFI_STATUS\r
365**/\r
366EFI_STATUS\r
367GetPvhMemmapEntries (\r
368 struct hvm_memmap_table_entry **Entries,\r
369 UINT32 *Count\r
370 )\r
371{\r
372 UINT32 *PVHResetVectorData;\r
373 struct hvm_start_info *pvh_start_info;\r
374\r
375 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);\r
376 if (PVHResetVectorData == 0) {\r
377 return EFI_NOT_FOUND;\r
378 }\r
379\r
380 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];\r
381\r
382 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;\r
383 *Count = pvh_start_info->memmap_entries;\r
384\r
385 return EFI_SUCCESS;\r
386}\r
387\r
388STATIC\r
389UINT64\r
390GetHighestSystemMemoryAddressFromPvhMemmap (\r
391 BOOLEAN Below4gb\r
392 )\r
393{\r
394 struct hvm_memmap_table_entry *Memmap;\r
395 UINT32 MemmapEntriesCount;\r
396 struct hvm_memmap_table_entry *Entry;\r
397 EFI_STATUS Status;\r
398 UINT32 Loop;\r
399 UINT64 HighestAddress;\r
400 UINT64 EntryEnd;\r
401\r
402 HighestAddress = 0;\r
403\r
404 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);\r
405 ASSERT_EFI_ERROR (Status);\r
406\r
407 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {\r
408 Entry = Memmap + Loop;\r
409 EntryEnd = Entry->addr + Entry->size;\r
410\r
411 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&\r
412 (EntryEnd > HighestAddress))\r
413 {\r
414 if (Below4gb && (EntryEnd <= BASE_4GB)) {\r
415 HighestAddress = EntryEnd;\r
416 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {\r
417 HighestAddress = EntryEnd;\r
418 }\r
419 }\r
420 }\r
421\r
422 return HighestAddress;\r
423}\r
424\r
124b7650 425VOID\r
10460942
MX
426EFIAPI\r
427PlatformGetSystemMemorySizeBelow4gb (\r
428 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
429 )\r
430{\r
431 EFI_STATUS Status;\r
10460942
MX
432 UINT8 Cmos0x34;\r
433 UINT8 Cmos0x35;\r
434\r
e03b0d06
SB
435 if ((PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) &&\r
436 (CcProbe () != CcGuestTypeIntelTdx))\r
437 {\r
10460942 438 // Get the information from PVH memmap\r
124b7650
GH
439 PlatformInfoHob->LowMemory = (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);\r
440 return;\r
10460942
MX
441 }\r
442\r
124b7650
GH
443 Status = PlatformScanE820 (PlatformGetLowMemoryCB, PlatformInfoHob);\r
444 if (!EFI_ERROR (Status) && (PlatformInfoHob->LowMemory > 0)) {\r
445 return;\r
10460942
MX
446 }\r
447\r
448 //\r
449 // CMOS 0x34/0x35 specifies the system memory above 16 MB.\r
450 // * CMOS(0x35) is the high byte\r
451 // * CMOS(0x34) is the low byte\r
452 // * The size is specified in 64kb chunks\r
453 // * Since this is memory above 16MB, the 16MB must be added\r
454 // into the calculation to get the total memory size.\r
455 //\r
456\r
457 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);\r
458 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);\r
459\r
124b7650 460 PlatformInfoHob->LowMemory = (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);\r
10460942
MX
461}\r
462\r
463STATIC\r
464UINT64\r
465PlatformGetSystemMemorySizeAbove4gb (\r
466 )\r
467{\r
468 UINT32 Size;\r
469 UINTN CmosIndex;\r
470\r
471 //\r
472 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.\r
473 // * CMOS(0x5d) is the most significant size byte\r
474 // * CMOS(0x5c) is the middle size byte\r
475 // * CMOS(0x5b) is the least significant size byte\r
476 // * The size is specified in 64kb chunks\r
477 //\r
478\r
479 Size = 0;\r
480 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {\r
481 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);\r
482 }\r
483\r
484 return LShiftU64 (Size, 16);\r
485}\r
486\r
487/**\r
488 Return the highest address that DXE could possibly use, plus one.\r
489**/\r
490STATIC\r
e0375304 491VOID\r
10460942
MX
492PlatformGetFirstNonAddress (\r
493 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
494 )\r
495{\r
10460942
MX
496 UINT32 FwCfgPciMmio64Mb;\r
497 EFI_STATUS Status;\r
498 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
499 UINTN FwCfgSize;\r
500 UINT64 HotPlugMemoryEnd;\r
501\r
10460942
MX
502 //\r
503 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM\r
504 // address from it. This can express an address >= 4GB+1TB.\r
505 //\r
506 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which\r
507 // can only express a size smaller than 1TB), and add it to 4GB.\r
508 //\r
e0375304
GH
509 PlatformInfoHob->FirstNonAddress = BASE_4GB;\r
510 Status = PlatformScanE820 (PlatformGetFirstNonAddressCB, PlatformInfoHob);\r
10460942 511 if (EFI_ERROR (Status)) {\r
e0375304 512 PlatformInfoHob->FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();\r
10460942
MX
513 }\r
514\r
515 //\r
516 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO\r
517 // resources to 32-bit anyway. See DegradeResource() in\r
518 // "PciResourceSupport.c".\r
519 //\r
520 #ifdef MDE_CPU_IA32\r
521 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
e0375304 522 return;\r
10460942
MX
523 }\r
524\r
525 #endif\r
526\r
527 //\r
528 // See if the user specified the number of megabytes for the 64-bit PCI host\r
529 // aperture. Accept an aperture size up to 16TB.\r
530 //\r
531 // As signaled by the "X-" prefix, this knob is experimental, and might go\r
532 // away at any time.\r
533 //\r
534 Status = QemuFwCfgParseUint32 (\r
535 "opt/ovmf/X-PciMmio64Mb",\r
536 FALSE,\r
537 &FwCfgPciMmio64Mb\r
538 );\r
539 switch (Status) {\r
540 case EFI_UNSUPPORTED:\r
541 case EFI_NOT_FOUND:\r
542 break;\r
543 case EFI_SUCCESS:\r
544 if (FwCfgPciMmio64Mb <= 0x1000000) {\r
545 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);\r
546 break;\r
547 }\r
548\r
549 //\r
550 // fall through\r
551 //\r
552 default:\r
553 DEBUG ((\r
554 DEBUG_WARN,\r
555 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",\r
556 __FUNCTION__\r
557 ));\r
558 break;\r
559 }\r
560\r
561 if (PlatformInfoHob->PcdPciMmio64Size == 0) {\r
562 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r
563 DEBUG ((\r
564 DEBUG_INFO,\r
565 "%a: disabling 64-bit PCI host aperture\n",\r
566 __FUNCTION__\r
567 ));\r
568 }\r
569\r
570 //\r
571 // There's nothing more to do; the amount of memory above 4GB fully\r
572 // determines the highest address plus one. The memory hotplug area (see\r
573 // below) plays no role for the firmware in this case.\r
574 //\r
e0375304 575 return;\r
10460942
MX
576 }\r
577\r
578 //\r
579 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an\r
580 // absolute, exclusive end address for the memory hotplug area. This area\r
581 // starts right at the end of the memory above 4GB. The 64-bit PCI host\r
582 // aperture must be placed above it.\r
583 //\r
584 Status = QemuFwCfgFindFile (\r
585 "etc/reserved-memory-end",\r
586 &FwCfgItem,\r
587 &FwCfgSize\r
588 );\r
589 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {\r
590 QemuFwCfgSelectItem (FwCfgItem);\r
591 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);\r
592 DEBUG ((\r
593 DEBUG_VERBOSE,\r
594 "%a: HotPlugMemoryEnd=0x%Lx\n",\r
595 __FUNCTION__,\r
596 HotPlugMemoryEnd\r
597 ));\r
598\r
e0375304
GH
599 ASSERT (HotPlugMemoryEnd >= PlatformInfoHob->FirstNonAddress);\r
600 PlatformInfoHob->FirstNonAddress = HotPlugMemoryEnd;\r
10460942
MX
601 }\r
602\r
603 //\r
604 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so\r
605 // that the host can map it with 1GB hugepages. Follow suit.\r
606 //\r
e0375304 607 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->FirstNonAddress, (UINT64)SIZE_1GB);\r
10460942
MX
608 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);\r
609\r
610 //\r
611 // The 64-bit PCI host aperture should also be "naturally" aligned. The\r
612 // alignment is determined by rounding the size of the aperture down to the\r
613 // next smaller or equal power of two. That is, align the aperture by the\r
614 // largest BAR size that can fit into it.\r
615 //\r
616 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));\r
617\r
618 //\r
619 // The useful address space ends with the 64-bit PCI host aperture.\r
620 //\r
e0375304
GH
621 PlatformInfoHob->FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;\r
622 return;\r
10460942
MX
623}\r
624\r
bd10d4e2 625/*\r
8f9ef9c9 626 * Use CPUID to figure physical address width.\r
bd10d4e2 627 *\r
8f9ef9c9
GH
628 * Does *not* work reliable on qemu. For historical reasons qemu\r
629 * returns phys-bits=40 by default even in case the host machine\r
630 * supports less than that.\r
631 *\r
632 * So we apply the following rules (which can be enabled/disabled\r
633 * using the QemuQuirk parameter) to figure whenever we can work with\r
634 * the returned physical address width or not:\r
635 *\r
636 * (1) If it is 41 or higher consider it valid.\r
637 * (2) If it is 40 or lower consider it valid in case it matches a\r
638 * known-good value for the CPU vendor, which is:\r
639 * -> 36 or 39 for Intel\r
640 * -> 40 for AMD\r
641 * (3) Otherwise consider it invalid.\r
642 *\r
643 * Recommendation: Run qemu with host-phys-bits=on. That will make\r
644 * sure guest phys-bits is not larger than host phys-bits. Some\r
645 * distro builds do that by default.\r
bd10d4e2
GH
646 */\r
647VOID\r
648EFIAPI\r
649PlatformAddressWidthFromCpuid (\r
8f9ef9c9
GH
650 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob,\r
651 IN BOOLEAN QemuQuirk\r
bd10d4e2
GH
652 )\r
653{\r
8f9ef9c9
GH
654 UINT32 RegEax, RegEbx, RegEcx, RegEdx, Max;\r
655 UINT8 PhysBits;\r
656 CHAR8 Signature[13] = { 0 };\r
657 BOOLEAN Valid = FALSE;\r
658 BOOLEAN Page1GSupport = FALSE;\r
659\r
660 AsmCpuid (0x80000000, &RegEax, &RegEbx, &RegEcx, &RegEdx);\r
661 *(UINT32 *)(Signature + 0) = RegEbx;\r
662 *(UINT32 *)(Signature + 4) = RegEdx;\r
663 *(UINT32 *)(Signature + 8) = RegEcx;\r
664 Max = RegEax;\r
665\r
666 if (Max >= 0x80000001) {\r
667 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
668 if ((RegEdx & BIT26) != 0) {\r
669 Page1GSupport = TRUE;\r
670 }\r
671 }\r
bd10d4e2 672\r
8f9ef9c9 673 if (Max >= 0x80000008) {\r
bd10d4e2 674 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
8f9ef9c9 675 PhysBits = (UINT8)RegEax;\r
bd10d4e2 676 } else {\r
8f9ef9c9 677 PhysBits = 36;\r
bd10d4e2
GH
678 }\r
679\r
8f9ef9c9
GH
680 if (!QemuQuirk) {\r
681 Valid = TRUE;\r
682 } else if (PhysBits >= 41) {\r
683 Valid = TRUE;\r
684 } else if (AsciiStrCmp (Signature, "GenuineIntel") == 0) {\r
685 if ((PhysBits == 36) || (PhysBits == 39)) {\r
686 Valid = TRUE;\r
687 }\r
688 } else if (AsciiStrCmp (Signature, "AuthenticAMD") == 0) {\r
689 if (PhysBits == 40) {\r
690 Valid = TRUE;\r
691 }\r
692 }\r
bd10d4e2
GH
693\r
694 DEBUG ((\r
695 DEBUG_INFO,\r
8f9ef9c9 696 "%a: Signature: '%a', PhysBits: %d, QemuQuirk: %a, Valid: %a\n",\r
bd10d4e2 697 __FUNCTION__,\r
8f9ef9c9
GH
698 Signature,\r
699 PhysBits,\r
700 QemuQuirk ? "On" : "Off",\r
701 Valid ? "Yes" : "No"\r
bd10d4e2 702 ));\r
8f9ef9c9
GH
703\r
704 if (Valid) {\r
705 if (PhysBits > 47) {\r
706 /*\r
707 * Avoid 5-level paging altogether for now, which limits\r
708 * PhysBits to 48. Also avoid using address bit 48, due to sign\r
709 * extension we can't identity-map these addresses (and lots of\r
710 * places in edk2 assume we have everything identity-mapped).\r
711 * So the actual limit is 47.\r
712 */\r
713 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 47 (avoid 5-level paging)\n", __func__));\r
714 PhysBits = 47;\r
715 }\r
716\r
717 if (!Page1GSupport && (PhysBits > 40)) {\r
718 DEBUG ((DEBUG_INFO, "%a: limit PhysBits to 40 (no 1G pages available)\n", __func__));\r
719 PhysBits = 40;\r
720 }\r
721\r
722 PlatformInfoHob->PhysMemAddressWidth = PhysBits;\r
723 PlatformInfoHob->FirstNonAddress = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);\r
724 }\r
bd10d4e2
GH
725}\r
726\r
ecb778d0
GH
727VOID\r
728EFIAPI\r
729PlatformDynamicMmioWindow (\r
730 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
731 )\r
732{\r
733 UINT64 AddrSpace, MmioSpace;\r
734\r
735 AddrSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth);\r
736 MmioSpace = LShiftU64 (1, PlatformInfoHob->PhysMemAddressWidth - 3);\r
737\r
738 if ((PlatformInfoHob->PcdPciMmio64Size < MmioSpace) &&\r
739 (PlatformInfoHob->PcdPciMmio64Base + MmioSpace < AddrSpace))\r
740 {\r
741 DEBUG ((DEBUG_INFO, "%a: using dynamic mmio window\n", __func__));\r
742 DEBUG ((DEBUG_INFO, "%a: Addr Space 0x%Lx (%Ld GB)\n", __func__, AddrSpace, RShiftU64 (AddrSpace, 30)));\r
743 DEBUG ((DEBUG_INFO, "%a: MMIO Space 0x%Lx (%Ld GB)\n", __func__, MmioSpace, RShiftU64 (MmioSpace, 30)));\r
744 PlatformInfoHob->PcdPciMmio64Size = MmioSpace;\r
745 PlatformInfoHob->PcdPciMmio64Base = AddrSpace - MmioSpace;\r
746 } else {\r
747 DEBUG ((DEBUG_INFO, "%a: using classic mmio window\n", __func__));\r
748 }\r
749\r
750 DEBUG ((DEBUG_INFO, "%a: Pci64 Base 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Base));\r
751 DEBUG ((DEBUG_INFO, "%a: Pci64 Size 0x%Lx\n", __func__, PlatformInfoHob->PcdPciMmio64Size));\r
752}\r
753\r
3497fd5c
NOL
754/**\r
755 Iterate over the PCI host bridges resources information optionally provided\r
756 in fw-cfg and find the highest address contained in the PCI MMIO windows. If\r
757 the information is found, return the exclusive end; one past the last usable\r
758 address.\r
759\r
760 @param[out] PciMmioAddressEnd Pointer to one-after End Address updated with\r
761 information extracted from host-provided data\r
762 or zero if no information available or an\r
763 error happened\r
764\r
765 @retval EFI_SUCCESS PCI information was read and the output\r
766 parameter updated with the last valid\r
767 address in the 64-bit MMIO range.\r
768 @retval EFI_INVALID_PARAMETER Pointer parameter is invalid\r
769 @retval EFI_INCOMPATIBLE_VERSION Hardware information found in fw-cfg\r
770 has an incompatible format\r
771 @retval EFI_UNSUPPORTED Fw-cfg is not supported, thus host\r
772 provided information, if any, cannot be\r
773 read\r
774 @retval EFI_NOT_FOUND No PCI host bridge information provided\r
775 by the host.\r
776**/\r
777STATIC\r
778EFI_STATUS\r
779PlatformScanHostProvided64BitPciMmioEnd (\r
780 OUT UINT64 *PciMmioAddressEnd\r
781 )\r
782{\r
783 EFI_STATUS Status;\r
784 HOST_BRIDGE_INFO HostBridge;\r
785 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
786 UINTN FwCfgSize;\r
787 UINTN FwCfgReadIndex;\r
788 UINTN ReadDataSize;\r
789 UINT64 Above4GMmioEnd;\r
790\r
791 if (PciMmioAddressEnd == NULL) {\r
792 return EFI_INVALID_PARAMETER;\r
793 }\r
794\r
795 *PciMmioAddressEnd = 0;\r
796 Above4GMmioEnd = 0;\r
797\r
798 Status = QemuFwCfgFindFile ("etc/hardware-info", &FwCfgItem, &FwCfgSize);\r
799 if (EFI_ERROR (Status)) {\r
800 return Status;\r
801 }\r
802\r
803 QemuFwCfgSelectItem (FwCfgItem);\r
804\r
805 FwCfgReadIndex = 0;\r
806 while (FwCfgReadIndex < FwCfgSize) {\r
807 Status = QemuFwCfgReadNextHardwareInfoByType (\r
808 HardwareInfoTypeHostBridge,\r
809 sizeof (HostBridge),\r
810 FwCfgSize,\r
811 &HostBridge,\r
812 &ReadDataSize,\r
813 &FwCfgReadIndex\r
814 );\r
815\r
816 if (Status != EFI_SUCCESS) {\r
817 //\r
818 // No more data available to read in the file, break\r
819 // loop and finish process\r
820 //\r
821 break;\r
822 }\r
823\r
824 Status = HardwareInfoPciHostBridgeLastMmioAddress (\r
825 &HostBridge,\r
826 ReadDataSize,\r
827 TRUE,\r
828 &Above4GMmioEnd\r
829 );\r
830\r
831 if (Status != EFI_SUCCESS) {\r
832 //\r
833 // Error parsing MMIO apertures and extracting last MMIO\r
834 // address, reset PciMmioAddressEnd as if no information was\r
835 // found, to avoid moving forward with incomplete data, and\r
836 // bail out\r
837 //\r
838 DEBUG ((\r
839 DEBUG_ERROR,\r
840 "%a: ignoring malformed hardware information from fw_cfg\n",\r
841 __FUNCTION__\r
842 ));\r
843 *PciMmioAddressEnd = 0;\r
844 return Status;\r
845 }\r
846\r
847 if (Above4GMmioEnd > *PciMmioAddressEnd) {\r
848 *PciMmioAddressEnd = Above4GMmioEnd;\r
849 }\r
850 }\r
851\r
852 if (*PciMmioAddressEnd > 0) {\r
853 //\r
854 // Host-provided PCI information was found and a MMIO window end\r
855 // derived from it.\r
856 // Increase the End address by one to have the output pointing to\r
857 // one after the address in use (exclusive end).\r
858 //\r
859 *PciMmioAddressEnd += 1;\r
860\r
861 DEBUG ((\r
862 DEBUG_INFO,\r
863 "%a: Pci64End=0x%Lx\n",\r
864 __FUNCTION__,\r
865 *PciMmioAddressEnd\r
866 ));\r
867\r
868 return EFI_SUCCESS;\r
869 }\r
870\r
871 return EFI_NOT_FOUND;\r
872}\r
873\r
10460942
MX
874/**\r
875 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.\r
876**/\r
877VOID\r
878EFIAPI\r
879PlatformAddressWidthInitialization (\r
880 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
881 )\r
882{\r
3497fd5c
NOL
883 UINT8 PhysMemAddressWidth;\r
884 EFI_STATUS Status;\r
10460942 885\r
bd10d4e2 886 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
8f9ef9c9 887 PlatformAddressWidthFromCpuid (PlatformInfoHob, FALSE);\r
bd10d4e2
GH
888 return;\r
889 }\r
890\r
10460942 891 //\r
3497fd5c
NOL
892 // First scan host-provided hardware information to assess if the address\r
893 // space is already known. If so, guest must use those values.\r
10460942 894 //\r
e0375304 895 Status = PlatformScanHostProvided64BitPciMmioEnd (&PlatformInfoHob->FirstNonAddress);\r
3497fd5c
NOL
896\r
897 if (EFI_ERROR (Status)) {\r
898 //\r
899 // If the host did not provide valid hardware information leading to a\r
900 // hard-defined 64-bit MMIO end, fold back to calculating the minimum range\r
901 // needed.\r
902 // As guest-physical memory size grows, the permanent PEI RAM requirements\r
903 // are dominated by the identity-mapping page tables built by the DXE IPL.\r
904 // The DXL IPL keys off of the physical address bits advertized in the CPU\r
905 // HOB. To conserve memory, we calculate the minimum address width here.\r
906 //\r
e0375304 907 PlatformGetFirstNonAddress (PlatformInfoHob);\r
3497fd5c
NOL
908 }\r
909\r
bbda386d
GH
910 PlatformAddressWidthFromCpuid (PlatformInfoHob, TRUE);\r
911 if (PlatformInfoHob->PhysMemAddressWidth != 0) {\r
912 // physical address width is known\r
ecb778d0 913 PlatformDynamicMmioWindow (PlatformInfoHob);\r
bbda386d
GH
914 return;\r
915 }\r
916\r
917 //\r
918 // physical address width is NOT known\r
919 // -> do some guess work, mostly based on installed memory\r
920 // -> try be conservstibe to stay below the guaranteed minimum of\r
921 // 36 phys bits (aka 64 GB).\r
922 //\r
e0375304 923 PhysMemAddressWidth = (UINT8)HighBitSet64 (PlatformInfoHob->FirstNonAddress);\r
10460942
MX
924\r
925 //\r
926 // If FirstNonAddress is not an integral power of two, then we need an\r
927 // additional bit.\r
928 //\r
e0375304 929 if ((PlatformInfoHob->FirstNonAddress & (PlatformInfoHob->FirstNonAddress - 1)) != 0) {\r
10460942
MX
930 ++PhysMemAddressWidth;\r
931 }\r
932\r
933 //\r
934 // The minimum address width is 36 (covers up to and excluding 64 GB, which\r
935 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for\r
936 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We\r
937 // can simply assert that here, since 48 bits are good enough for 256 TB.\r
938 //\r
939 if (PhysMemAddressWidth <= 36) {\r
940 PhysMemAddressWidth = 36;\r
941 }\r
942\r
e23f8f52
MX
943 #if defined (MDE_CPU_X64)\r
944 if (TdIsEnabled ()) {\r
945 if (TdSharedPageMask () == (1ULL << 47)) {\r
946 PhysMemAddressWidth = 48;\r
947 } else {\r
948 PhysMemAddressWidth = 52;\r
949 }\r
950 }\r
951\r
952 ASSERT (PhysMemAddressWidth <= 52);\r
953 #else\r
10460942 954 ASSERT (PhysMemAddressWidth <= 48);\r
e23f8f52 955 #endif\r
10460942 956\r
10460942
MX
957 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;\r
958}\r
959\r
960STATIC\r
961VOID\r
962QemuInitializeRamBelow1gb (\r
963 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
964 )\r
965{\r
966 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {\r
967 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);\r
968 PlatformAddReservedMemoryBaseSizeHob (\r
969 SMM_DEFAULT_SMBASE,\r
970 MCH_DEFAULT_SMBASE_SIZE,\r
971 TRUE /* Cacheable */\r
972 );\r
973 STATIC_ASSERT (\r
974 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,\r
975 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"\r
976 );\r
977 PlatformAddMemoryRangeHob (\r
978 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,\r
979 BASE_512KB + BASE_128KB\r
980 );\r
981 } else {\r
982 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);\r
983 }\r
984}\r
985\r
986/**\r
987 Peform Memory Detection for QEMU / KVM\r
988\r
989**/\r
990VOID\r
991EFIAPI\r
992PlatformQemuInitializeRam (\r
993 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
994 )\r
995{\r
10460942
MX
996 UINT64 UpperMemorySize;\r
997 MTRR_SETTINGS MtrrSettings;\r
998 EFI_STATUS Status;\r
999\r
1000 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));\r
1001\r
1002 //\r
1003 // Determine total memory size available\r
1004 //\r
124b7650 1005 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
10460942
MX
1006\r
1007 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {\r
1008 //\r
1009 // Create the following memory HOB as an exception on the S3 boot path.\r
1010 //\r
1011 // Normally we'd create memory HOBs only on the normal boot path. However,\r
1012 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as\r
1013 // well, for "borrowing" a subset of it temporarily, for the AP startup\r
1014 // vector.\r
1015 //\r
1016 // CpuMpPei saves the original contents of the borrowed area in permanent\r
1017 // PEI RAM, in a backup buffer allocated with the normal PEI services.\r
1018 // CpuMpPei restores the original contents ("returns" the borrowed area) at\r
1019 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before\r
1020 // transferring control to the OS's wakeup vector in the FACS.\r
1021 //\r
1022 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to\r
1023 // restore the original contents. Furthermore, we expect all such PEIMs\r
1024 // (CpuMpPei included) to claim the borrowed areas by producing memory\r
1025 // allocation HOBs, and to honor preexistent memory allocation HOBs when\r
1026 // looking for an area to borrow.\r
1027 //\r
1028 QemuInitializeRamBelow1gb (PlatformInfoHob);\r
1029 } else {\r
1030 //\r
1031 // Create memory HOBs\r
1032 //\r
1033 QemuInitializeRamBelow1gb (PlatformInfoHob);\r
1034\r
1035 if (PlatformInfoHob->SmmSmramRequire) {\r
1036 UINT32 TsegSize;\r
1037\r
1038 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;\r
124b7650 1039 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory - TsegSize);\r
10460942 1040 PlatformAddReservedMemoryBaseSizeHob (\r
124b7650 1041 PlatformInfoHob->LowMemory - TsegSize,\r
10460942
MX
1042 TsegSize,\r
1043 TRUE\r
1044 );\r
1045 } else {\r
124b7650 1046 PlatformAddMemoryRangeHob (BASE_1MB, PlatformInfoHob->LowMemory);\r
10460942
MX
1047 }\r
1048\r
1049 //\r
1050 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM\r
1051 // entries. Otherwise, create a single memory HOB with the flat >=4GB\r
1052 // memory size read from the CMOS.\r
1053 //\r
1054 Status = PlatformScanOrAdd64BitE820Ram (TRUE, NULL, NULL);\r
1055 if (EFI_ERROR (Status)) {\r
1056 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();\r
1057 if (UpperMemorySize != 0) {\r
1058 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);\r
1059 }\r
1060 }\r
1061 }\r
1062\r
1063 //\r
1064 // We'd like to keep the following ranges uncached:\r
1065 // - [640 KB, 1 MB)\r
85fd05ab 1066 // - [Uc32Base, 4 GB)\r
10460942
MX
1067 //\r
1068 // Everything else should be WB. Unfortunately, programming the inverse (ie.\r
1069 // keeping the default UC, and configuring the complement set of the above as\r
1070 // WB) is not reliable in general, because the end of the upper RAM can have\r
1071 // practically any alignment, and we may not have enough variable MTRRs to\r
1072 // cover it exactly.\r
1073 //\r
1074 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {\r
1075 MtrrGetAllMtrrs (&MtrrSettings);\r
1076\r
1077 //\r
1078 // MTRRs disabled, fixed MTRRs disabled, default type is uncached\r
1079 //\r
1080 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);\r
1081 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);\r
1082 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);\r
1083\r
1084 //\r
1085 // flip default type to writeback\r
1086 //\r
1087 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);\r
1088 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);\r
1089 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;\r
1090 MtrrSetAllMtrrs (&MtrrSettings);\r
1091\r
1092 //\r
1093 // Set memory range from 640KB to 1MB to uncacheable\r
1094 //\r
1095 Status = MtrrSetMemoryAttribute (\r
1096 BASE_512KB + BASE_128KB,\r
1097 BASE_1MB - (BASE_512KB + BASE_128KB),\r
1098 CacheUncacheable\r
1099 );\r
1100 ASSERT_EFI_ERROR (Status);\r
1101\r
1102 //\r
1103 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI\r
1104 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.\r
1105 //\r
1106 Status = MtrrSetMemoryAttribute (\r
1107 PlatformInfoHob->Uc32Base,\r
1108 SIZE_4GB - PlatformInfoHob->Uc32Base,\r
1109 CacheUncacheable\r
1110 );\r
1111 ASSERT_EFI_ERROR (Status);\r
1112 }\r
1113}\r
1114\r
1115VOID\r
1116EFIAPI\r
1117PlatformQemuInitializeRamForS3 (\r
1118 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
1119 )\r
1120{\r
1121 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {\r
1122 //\r
1123 // This is the memory range that will be used for PEI on S3 resume\r
1124 //\r
1125 BuildMemoryAllocationHob (\r
1126 PlatformInfoHob->S3AcpiReservedMemoryBase,\r
1127 PlatformInfoHob->S3AcpiReservedMemorySize,\r
1128 EfiACPIMemoryNVS\r
1129 );\r
1130\r
1131 //\r
1132 // Cover the initial RAM area used as stack and temporary PEI heap.\r
1133 //\r
1134 // This is reserved as ACPI NVS so it can be used on S3 resume.\r
1135 //\r
1136 BuildMemoryAllocationHob (\r
1137 PcdGet32 (PcdOvmfSecPeiTempRamBase),\r
1138 PcdGet32 (PcdOvmfSecPeiTempRamSize),\r
1139 EfiACPIMemoryNVS\r
1140 );\r
1141\r
1142 //\r
1143 // SEC stores its table of GUIDed section handlers here.\r
1144 //\r
1145 BuildMemoryAllocationHob (\r
1146 PcdGet64 (PcdGuidedExtractHandlerTableAddress),\r
1147 PcdGet32 (PcdGuidedExtractHandlerTableSize),\r
1148 EfiACPIMemoryNVS\r
1149 );\r
1150\r
1151 #ifdef MDE_CPU_X64\r
1152 //\r
1153 // Reserve the initial page tables built by the reset vector code.\r
1154 //\r
1155 // Since this memory range will be used by the Reset Vector on S3\r
1156 // resume, it must be reserved as ACPI NVS.\r
1157 //\r
1158 BuildMemoryAllocationHob (\r
1159 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),\r
1160 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),\r
1161 EfiACPIMemoryNVS\r
1162 );\r
1163\r
1164 if (PlatformInfoHob->SevEsIsEnabled) {\r
1165 //\r
1166 // If SEV-ES is enabled, reserve the GHCB-related memory area. This\r
1167 // includes the extra page table used to break down the 2MB page\r
1168 // mapping into 4KB page entries where the GHCB resides and the\r
1169 // GHCB area itself.\r
1170 //\r
1171 // Since this memory range will be used by the Reset Vector on S3\r
1172 // resume, it must be reserved as ACPI NVS.\r
1173 //\r
1174 BuildMemoryAllocationHob (\r
1175 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),\r
1176 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),\r
1177 EfiACPIMemoryNVS\r
1178 );\r
1179 BuildMemoryAllocationHob (\r
1180 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),\r
1181 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),\r
1182 EfiACPIMemoryNVS\r
1183 );\r
1184 BuildMemoryAllocationHob (\r
1185 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),\r
1186 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),\r
1187 EfiACPIMemoryNVS\r
1188 );\r
1189 }\r
1190\r
1191 #endif\r
1192 }\r
1193\r
1194 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {\r
1195 if (!PlatformInfoHob->SmmSmramRequire) {\r
1196 //\r
1197 // Reserve the lock box storage area\r
1198 //\r
1199 // Since this memory range will be used on S3 resume, it must be\r
1200 // reserved as ACPI NVS.\r
1201 //\r
1202 // If S3 is unsupported, then various drivers might still write to the\r
1203 // LockBox area. We ought to prevent DXE from serving allocation requests\r
1204 // such that they would overlap the LockBox storage.\r
1205 //\r
1206 ZeroMem (\r
1207 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r
1208 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)\r
1209 );\r
1210 BuildMemoryAllocationHob (\r
1211 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),\r
1212 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),\r
1213 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
1214 );\r
1215 }\r
1216\r
1217 if (PlatformInfoHob->SmmSmramRequire) {\r
1218 UINT32 TsegSize;\r
1219\r
1220 //\r
1221 // Make sure the TSEG area that we reported as a reserved memory resource\r
1222 // cannot be used for reserved memory allocations.\r
1223 //\r
124b7650 1224 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
10460942
MX
1225 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;\r
1226 BuildMemoryAllocationHob (\r
124b7650 1227 PlatformInfoHob->LowMemory - TsegSize,\r
10460942
MX
1228 TsegSize,\r
1229 EfiReservedMemoryType\r
1230 );\r
1231 //\r
1232 // Similarly, allocate away the (already reserved) SMRAM at the default\r
1233 // SMBASE, if it exists.\r
1234 //\r
1235 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {\r
1236 BuildMemoryAllocationHob (\r
1237 SMM_DEFAULT_SMBASE,\r
1238 MCH_DEFAULT_SMBASE_SIZE,\r
1239 EfiReservedMemoryType\r
1240 );\r
1241 }\r
1242 }\r
1243\r
1244 #ifdef MDE_CPU_X64\r
1245 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {\r
1246 //\r
1247 // Reserve the work area.\r
1248 //\r
1249 // Since this memory range will be used by the Reset Vector on S3\r
1250 // resume, it must be reserved as ACPI NVS.\r
1251 //\r
1252 // If S3 is unsupported, then various drivers might still write to the\r
1253 // work area. We ought to prevent DXE from serving allocation requests\r
1254 // such that they would overlap the work area.\r
1255 //\r
1256 BuildMemoryAllocationHob (\r
1257 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),\r
1258 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),\r
1259 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData\r
1260 );\r
1261 }\r
1262\r
1263 #endif\r
1264 }\r
1265}\r