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