]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformPei/MemDetect.c
OvmfPkg/PlatformPei: Refactor MemMapInitialization
[mirror_edk2.git] / OvmfPkg / PlatformPei / MemDetect.c
1 /**@file
2 Memory Detection for Virtual Machines.
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 Module Name:
8
9 MemDetect.c
10
11 **/
12
13 //
14 // The package level header files this module uses
15 //
16 #include <IndustryStandard/E820.h>
17 #include <IndustryStandard/I440FxPiix4.h>
18 #include <IndustryStandard/Q35MchIch9.h>
19 #include <IndustryStandard/CloudHv.h>
20 #include <IndustryStandard/Xen/arch-x86/hvm/start_info.h>
21 #include <PiPei.h>
22 #include <Register/Intel/SmramSaveStateMap.h>
23
24 //
25 // The Library classes this module consumes
26 //
27 #include <Library/BaseLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DebugLib.h>
30 #include <Library/HobLib.h>
31 #include <Library/IoLib.h>
32 #include <Library/MemEncryptSevLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/PciLib.h>
35 #include <Library/PeimEntryPoint.h>
36 #include <Library/ResourcePublicationLib.h>
37 #include <Library/MtrrLib.h>
38 #include <Library/QemuFwCfgLib.h>
39 #include <Library/QemuFwCfgSimpleParserLib.h>
40
41 #include "Platform.h"
42
43 VOID
44 Q35TsegMbytesInitialization (
45 VOID
46 )
47 {
48 UINT16 ExtendedTsegMbytes;
49 RETURN_STATUS PcdStatus;
50
51 ASSERT (mPlatformInfoHob.HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
52
53 //
54 // Check if QEMU offers an extended TSEG.
55 //
56 // This can be seen from writing MCH_EXT_TSEG_MB_QUERY to the MCH_EXT_TSEG_MB
57 // register, and reading back the register.
58 //
59 // On a QEMU machine type that does not offer an extended TSEG, the initial
60 // write overwrites whatever value a malicious guest OS may have placed in
61 // the (unimplemented) register, before entering S3 or rebooting.
62 // Subsequently, the read returns MCH_EXT_TSEG_MB_QUERY unchanged.
63 //
64 // On a QEMU machine type that offers an extended TSEG, the initial write
65 // triggers an update to the register. Subsequently, the value read back
66 // (which is guaranteed to differ from MCH_EXT_TSEG_MB_QUERY) tells us the
67 // number of megabytes.
68 //
69 PciWrite16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB), MCH_EXT_TSEG_MB_QUERY);
70 ExtendedTsegMbytes = PciRead16 (DRAMC_REGISTER_Q35 (MCH_EXT_TSEG_MB));
71 if (ExtendedTsegMbytes == MCH_EXT_TSEG_MB_QUERY) {
72 mPlatformInfoHob.Q35TsegMbytes = PcdGet16 (PcdQ35TsegMbytes);
73 return;
74 }
75
76 DEBUG ((
77 DEBUG_INFO,
78 "%a: QEMU offers an extended TSEG (%d MB)\n",
79 __FUNCTION__,
80 ExtendedTsegMbytes
81 ));
82 PcdStatus = PcdSet16S (PcdQ35TsegMbytes, ExtendedTsegMbytes);
83 ASSERT_RETURN_ERROR (PcdStatus);
84 mPlatformInfoHob.Q35TsegMbytes = ExtendedTsegMbytes;
85 }
86
87 VOID
88 Q35SmramAtDefaultSmbaseInitialization (
89 VOID
90 )
91 {
92 RETURN_STATUS PcdStatus;
93
94 ASSERT (mPlatformInfoHob.HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID);
95
96 mPlatformInfoHob.Q35SmramAtDefaultSmbase = FALSE;
97 if (FeaturePcdGet (PcdCsmEnable)) {
98 DEBUG ((
99 DEBUG_INFO,
100 "%a: SMRAM at default SMBASE not checked due to CSM\n",
101 __FUNCTION__
102 ));
103 } else {
104 UINTN CtlReg;
105 UINT8 CtlRegVal;
106
107 CtlReg = DRAMC_REGISTER_Q35 (MCH_DEFAULT_SMBASE_CTL);
108 PciWrite8 (CtlReg, MCH_DEFAULT_SMBASE_QUERY);
109 CtlRegVal = PciRead8 (CtlReg);
110 mPlatformInfoHob.Q35SmramAtDefaultSmbase = (BOOLEAN)(CtlRegVal ==
111 MCH_DEFAULT_SMBASE_IN_RAM);
112 DEBUG ((
113 DEBUG_INFO,
114 "%a: SMRAM at default SMBASE %a\n",
115 __FUNCTION__,
116 mPlatformInfoHob.Q35SmramAtDefaultSmbase ? "found" : "not found"
117 ));
118 }
119
120 PcdStatus = PcdSetBoolS (
121 PcdQ35SmramAtDefaultSmbase,
122 mPlatformInfoHob.Q35SmramAtDefaultSmbase
123 );
124 ASSERT_RETURN_ERROR (PcdStatus);
125 }
126
127 VOID
128 EFIAPI
129 PlatformQemuUc32BaseInitialization (
130 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
131 )
132 {
133 UINT32 LowerMemorySize;
134
135 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
136 return;
137 }
138
139 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
140 //
141 // On q35, the 32-bit area that we'll mark as UC, through variable MTRRs,
142 // starts at PcdPciExpressBaseAddress. The platform DSC is responsible for
143 // setting PcdPciExpressBaseAddress such that describing the
144 // [PcdPciExpressBaseAddress, 4GB) range require a very small number of
145 // variable MTRRs (preferably 1 or 2).
146 //
147 ASSERT (FixedPcdGet64 (PcdPciExpressBaseAddress) <= MAX_UINT32);
148 PlatformInfoHob->Uc32Base = (UINT32)FixedPcdGet64 (PcdPciExpressBaseAddress);
149 return;
150 }
151
152 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
153 PlatformInfoHob->Uc32Size = CLOUDHV_MMIO_HOLE_SIZE;
154 PlatformInfoHob->Uc32Base = CLOUDHV_MMIO_HOLE_ADDRESS;
155 return;
156 }
157
158 ASSERT (PlatformInfoHob->HostBridgeDevId == INTEL_82441_DEVICE_ID);
159 //
160 // On i440fx, start with the [LowerMemorySize, 4GB) range. Make sure one
161 // variable MTRR suffices by truncating the size to a whole power of two,
162 // while keeping the end affixed to 4GB. This will round the base up.
163 //
164 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
165 PlatformInfoHob->Uc32Size = GetPowerOfTwo32 ((UINT32)(SIZE_4GB - LowerMemorySize));
166 PlatformInfoHob->Uc32Base = (UINT32)(SIZE_4GB - PlatformInfoHob->Uc32Size);
167 //
168 // Assuming that LowerMemorySize is at least 1 byte, Uc32Size is at most 2GB.
169 // Therefore mQemuUc32Base is at least 2GB.
170 //
171 ASSERT (PlatformInfoHob->Uc32Base >= BASE_2GB);
172
173 if (PlatformInfoHob->Uc32Base != LowerMemorySize) {
174 DEBUG ((
175 DEBUG_VERBOSE,
176 "%a: rounded UC32 base from 0x%x up to 0x%x, for "
177 "an UC32 size of 0x%x\n",
178 __FUNCTION__,
179 LowerMemorySize,
180 PlatformInfoHob->Uc32Base,
181 PlatformInfoHob->Uc32Size
182 ));
183 }
184 }
185
186 /**
187 Iterate over the RAM entries in QEMU's fw_cfg E820 RAM map that start outside
188 of the 32-bit address range.
189
190 Find the highest exclusive >=4GB RAM address, or produce memory resource
191 descriptor HOBs for RAM entries that start at or above 4GB.
192
193 @param[out] MaxAddress If MaxAddress is NULL, then PlatformScanOrAdd64BitE820Ram()
194 produces memory resource descriptor HOBs for RAM
195 entries that start at or above 4GB.
196
197 Otherwise, MaxAddress holds the highest exclusive
198 >=4GB RAM address on output. If QEMU's fw_cfg E820
199 RAM map contains no RAM entry that starts outside of
200 the 32-bit address range, then MaxAddress is exactly
201 4GB on output.
202
203 @retval EFI_SUCCESS The fw_cfg E820 RAM map was found and processed.
204
205 @retval EFI_PROTOCOL_ERROR The RAM map was found, but its size wasn't a
206 whole multiple of sizeof(EFI_E820_ENTRY64). No
207 RAM entry was processed.
208
209 @return Error codes from QemuFwCfgFindFile(). No RAM
210 entry was processed.
211 **/
212 STATIC
213 EFI_STATUS
214 PlatformScanOrAdd64BitE820Ram (
215 IN BOOLEAN AddHighHob,
216 OUT UINT64 *LowMemory OPTIONAL,
217 OUT UINT64 *MaxAddress OPTIONAL
218 )
219 {
220 EFI_STATUS Status;
221 FIRMWARE_CONFIG_ITEM FwCfgItem;
222 UINTN FwCfgSize;
223 EFI_E820_ENTRY64 E820Entry;
224 UINTN Processed;
225
226 Status = QemuFwCfgFindFile ("etc/e820", &FwCfgItem, &FwCfgSize);
227 if (EFI_ERROR (Status)) {
228 return Status;
229 }
230
231 if (FwCfgSize % sizeof E820Entry != 0) {
232 return EFI_PROTOCOL_ERROR;
233 }
234
235 if (LowMemory != NULL) {
236 *LowMemory = 0;
237 }
238
239 if (MaxAddress != NULL) {
240 *MaxAddress = BASE_4GB;
241 }
242
243 QemuFwCfgSelectItem (FwCfgItem);
244 for (Processed = 0; Processed < FwCfgSize; Processed += sizeof E820Entry) {
245 QemuFwCfgReadBytes (sizeof E820Entry, &E820Entry);
246 DEBUG ((
247 DEBUG_VERBOSE,
248 "%a: Base=0x%Lx Length=0x%Lx Type=%u\n",
249 __FUNCTION__,
250 E820Entry.BaseAddr,
251 E820Entry.Length,
252 E820Entry.Type
253 ));
254 if (E820Entry.Type == EfiAcpiAddressRangeMemory) {
255 if (AddHighHob && (E820Entry.BaseAddr >= BASE_4GB)) {
256 UINT64 Base;
257 UINT64 End;
258
259 //
260 // Round up the start address, and round down the end address.
261 //
262 Base = ALIGN_VALUE (E820Entry.BaseAddr, (UINT64)EFI_PAGE_SIZE);
263 End = (E820Entry.BaseAddr + E820Entry.Length) &
264 ~(UINT64)EFI_PAGE_MASK;
265 if (Base < End) {
266 PlatformAddMemoryRangeHob (Base, End);
267 DEBUG ((
268 DEBUG_VERBOSE,
269 "%a: PlatformAddMemoryRangeHob [0x%Lx, 0x%Lx)\n",
270 __FUNCTION__,
271 Base,
272 End
273 ));
274 }
275 }
276
277 if (MaxAddress || LowMemory) {
278 UINT64 Candidate;
279
280 Candidate = E820Entry.BaseAddr + E820Entry.Length;
281 if (MaxAddress && (Candidate > *MaxAddress)) {
282 *MaxAddress = Candidate;
283 DEBUG ((
284 DEBUG_VERBOSE,
285 "%a: MaxAddress=0x%Lx\n",
286 __FUNCTION__,
287 *MaxAddress
288 ));
289 }
290
291 if (LowMemory && (Candidate > *LowMemory) && (Candidate < BASE_4GB)) {
292 *LowMemory = Candidate;
293 DEBUG ((
294 DEBUG_VERBOSE,
295 "%a: LowMemory=0x%Lx\n",
296 __FUNCTION__,
297 *LowMemory
298 ));
299 }
300 }
301 }
302 }
303
304 return EFI_SUCCESS;
305 }
306
307 /**
308 Returns PVH memmap
309
310 @param Entries Pointer to PVH memmap
311 @param Count Number of entries
312
313 @return EFI_STATUS
314 **/
315 EFI_STATUS
316 GetPvhMemmapEntries (
317 struct hvm_memmap_table_entry **Entries,
318 UINT32 *Count
319 )
320 {
321 UINT32 *PVHResetVectorData;
322 struct hvm_start_info *pvh_start_info;
323
324 PVHResetVectorData = (VOID *)(UINTN)PcdGet32 (PcdXenPvhStartOfDayStructPtr);
325 if (PVHResetVectorData == 0) {
326 return EFI_NOT_FOUND;
327 }
328
329 pvh_start_info = (struct hvm_start_info *)(UINTN)PVHResetVectorData[0];
330
331 *Entries = (struct hvm_memmap_table_entry *)(UINTN)pvh_start_info->memmap_paddr;
332 *Count = pvh_start_info->memmap_entries;
333
334 return EFI_SUCCESS;
335 }
336
337 STATIC
338 UINT64
339 GetHighestSystemMemoryAddressFromPvhMemmap (
340 BOOLEAN Below4gb
341 )
342 {
343 struct hvm_memmap_table_entry *Memmap;
344 UINT32 MemmapEntriesCount;
345 struct hvm_memmap_table_entry *Entry;
346 EFI_STATUS Status;
347 UINT32 Loop;
348 UINT64 HighestAddress;
349 UINT64 EntryEnd;
350
351 HighestAddress = 0;
352
353 Status = GetPvhMemmapEntries (&Memmap, &MemmapEntriesCount);
354 ASSERT_EFI_ERROR (Status);
355
356 for (Loop = 0; Loop < MemmapEntriesCount; Loop++) {
357 Entry = Memmap + Loop;
358 EntryEnd = Entry->addr + Entry->size;
359
360 if ((Entry->type == XEN_HVM_MEMMAP_TYPE_RAM) &&
361 (EntryEnd > HighestAddress))
362 {
363 if (Below4gb && (EntryEnd <= BASE_4GB)) {
364 HighestAddress = EntryEnd;
365 } else if (!Below4gb && (EntryEnd >= BASE_4GB)) {
366 HighestAddress = EntryEnd;
367 }
368 }
369 }
370
371 return HighestAddress;
372 }
373
374 UINT32
375 EFIAPI
376 PlatformGetSystemMemorySizeBelow4gb (
377 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
378 )
379 {
380 EFI_STATUS Status;
381 UINT64 LowerMemorySize = 0;
382 UINT8 Cmos0x34;
383 UINT8 Cmos0x35;
384
385 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
386 // Get the information from PVH memmap
387 return (UINT32)GetHighestSystemMemoryAddressFromPvhMemmap (TRUE);
388 }
389
390 Status = PlatformScanOrAdd64BitE820Ram (FALSE, &LowerMemorySize, NULL);
391 if ((Status == EFI_SUCCESS) && (LowerMemorySize > 0)) {
392 return (UINT32)LowerMemorySize;
393 }
394
395 //
396 // CMOS 0x34/0x35 specifies the system memory above 16 MB.
397 // * CMOS(0x35) is the high byte
398 // * CMOS(0x34) is the low byte
399 // * The size is specified in 64kb chunks
400 // * Since this is memory above 16MB, the 16MB must be added
401 // into the calculation to get the total memory size.
402 //
403
404 Cmos0x34 = (UINT8)PlatformCmosRead8 (0x34);
405 Cmos0x35 = (UINT8)PlatformCmosRead8 (0x35);
406
407 return (UINT32)(((UINTN)((Cmos0x35 << 8) + Cmos0x34) << 16) + SIZE_16MB);
408 }
409
410 STATIC
411 UINT64
412 PlatformGetSystemMemorySizeAbove4gb (
413 )
414 {
415 UINT32 Size;
416 UINTN CmosIndex;
417
418 //
419 // CMOS 0x5b-0x5d specifies the system memory above 4GB MB.
420 // * CMOS(0x5d) is the most significant size byte
421 // * CMOS(0x5c) is the middle size byte
422 // * CMOS(0x5b) is the least significant size byte
423 // * The size is specified in 64kb chunks
424 //
425
426 Size = 0;
427 for (CmosIndex = 0x5d; CmosIndex >= 0x5b; CmosIndex--) {
428 Size = (UINT32)(Size << 8) + (UINT32)PlatformCmosRead8 (CmosIndex);
429 }
430
431 return LShiftU64 (Size, 16);
432 }
433
434 /**
435 Return the highest address that DXE could possibly use, plus one.
436 **/
437 STATIC
438 UINT64
439 PlatformGetFirstNonAddress (
440 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
441 )
442 {
443 UINT64 FirstNonAddress;
444 UINT32 FwCfgPciMmio64Mb;
445 EFI_STATUS Status;
446 FIRMWARE_CONFIG_ITEM FwCfgItem;
447 UINTN FwCfgSize;
448 UINT64 HotPlugMemoryEnd;
449
450 //
451 // set FirstNonAddress to suppress incorrect compiler/analyzer warnings
452 //
453 FirstNonAddress = 0;
454
455 //
456 // If QEMU presents an E820 map, then get the highest exclusive >=4GB RAM
457 // address from it. This can express an address >= 4GB+1TB.
458 //
459 // Otherwise, get the flat size of the memory above 4GB from the CMOS (which
460 // can only express a size smaller than 1TB), and add it to 4GB.
461 //
462 Status = PlatformScanOrAdd64BitE820Ram (FALSE, NULL, &FirstNonAddress);
463 if (EFI_ERROR (Status)) {
464 FirstNonAddress = BASE_4GB + PlatformGetSystemMemorySizeAbove4gb ();
465 }
466
467 //
468 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
469 // resources to 32-bit anyway. See DegradeResource() in
470 // "PciResourceSupport.c".
471 //
472 #ifdef MDE_CPU_IA32
473 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
474 return FirstNonAddress;
475 }
476
477 #endif
478
479 //
480 // See if the user specified the number of megabytes for the 64-bit PCI host
481 // aperture. Accept an aperture size up to 16TB.
482 //
483 // As signaled by the "X-" prefix, this knob is experimental, and might go
484 // away at any time.
485 //
486 Status = QemuFwCfgParseUint32 (
487 "opt/ovmf/X-PciMmio64Mb",
488 FALSE,
489 &FwCfgPciMmio64Mb
490 );
491 switch (Status) {
492 case EFI_UNSUPPORTED:
493 case EFI_NOT_FOUND:
494 break;
495 case EFI_SUCCESS:
496 if (FwCfgPciMmio64Mb <= 0x1000000) {
497 PlatformInfoHob->PcdPciMmio64Size = LShiftU64 (FwCfgPciMmio64Mb, 20);
498 break;
499 }
500
501 //
502 // fall through
503 //
504 default:
505 DEBUG ((
506 DEBUG_WARN,
507 "%a: ignoring malformed 64-bit PCI host aperture size from fw_cfg\n",
508 __FUNCTION__
509 ));
510 break;
511 }
512
513 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
514 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
515 DEBUG ((
516 DEBUG_INFO,
517 "%a: disabling 64-bit PCI host aperture\n",
518 __FUNCTION__
519 ));
520 }
521
522 //
523 // There's nothing more to do; the amount of memory above 4GB fully
524 // determines the highest address plus one. The memory hotplug area (see
525 // below) plays no role for the firmware in this case.
526 //
527 return FirstNonAddress;
528 }
529
530 //
531 // The "etc/reserved-memory-end" fw_cfg file, when present, contains an
532 // absolute, exclusive end address for the memory hotplug area. This area
533 // starts right at the end of the memory above 4GB. The 64-bit PCI host
534 // aperture must be placed above it.
535 //
536 Status = QemuFwCfgFindFile (
537 "etc/reserved-memory-end",
538 &FwCfgItem,
539 &FwCfgSize
540 );
541 if (!EFI_ERROR (Status) && (FwCfgSize == sizeof HotPlugMemoryEnd)) {
542 QemuFwCfgSelectItem (FwCfgItem);
543 QemuFwCfgReadBytes (FwCfgSize, &HotPlugMemoryEnd);
544 DEBUG ((
545 DEBUG_VERBOSE,
546 "%a: HotPlugMemoryEnd=0x%Lx\n",
547 __FUNCTION__,
548 HotPlugMemoryEnd
549 ));
550
551 ASSERT (HotPlugMemoryEnd >= FirstNonAddress);
552 FirstNonAddress = HotPlugMemoryEnd;
553 }
554
555 //
556 // SeaBIOS aligns both boundaries of the 64-bit PCI host aperture to 1GB, so
557 // that the host can map it with 1GB hugepages. Follow suit.
558 //
559 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (FirstNonAddress, (UINT64)SIZE_1GB);
560 PlatformInfoHob->PcdPciMmio64Size = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Size, (UINT64)SIZE_1GB);
561
562 //
563 // The 64-bit PCI host aperture should also be "naturally" aligned. The
564 // alignment is determined by rounding the size of the aperture down to the
565 // next smaller or equal power of two. That is, align the aperture by the
566 // largest BAR size that can fit into it.
567 //
568 PlatformInfoHob->PcdPciMmio64Base = ALIGN_VALUE (PlatformInfoHob->PcdPciMmio64Base, GetPowerOfTwo64 (PlatformInfoHob->PcdPciMmio64Size));
569
570 //
571 // The useful address space ends with the 64-bit PCI host aperture.
572 //
573 FirstNonAddress = PlatformInfoHob->PcdPciMmio64Base + PlatformInfoHob->PcdPciMmio64Size;
574 return FirstNonAddress;
575 }
576
577 /**
578 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
579 **/
580 VOID
581 EFIAPI
582 PlatformAddressWidthInitialization (
583 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
584 )
585 {
586 UINT64 FirstNonAddress;
587 UINT8 PhysMemAddressWidth;
588
589 //
590 // As guest-physical memory size grows, the permanent PEI RAM requirements
591 // are dominated by the identity-mapping page tables built by the DXE IPL.
592 // The DXL IPL keys off of the physical address bits advertized in the CPU
593 // HOB. To conserve memory, we calculate the minimum address width here.
594 //
595 FirstNonAddress = PlatformGetFirstNonAddress (PlatformInfoHob);
596 PhysMemAddressWidth = (UINT8)HighBitSet64 (FirstNonAddress);
597
598 //
599 // If FirstNonAddress is not an integral power of two, then we need an
600 // additional bit.
601 //
602 if ((FirstNonAddress & (FirstNonAddress - 1)) != 0) {
603 ++PhysMemAddressWidth;
604 }
605
606 //
607 // The minimum address width is 36 (covers up to and excluding 64 GB, which
608 // is the maximum for Ia32 + PAE). The theoretical architecture maximum for
609 // X64 long mode is 52 bits, but the DXE IPL clamps that down to 48 bits. We
610 // can simply assert that here, since 48 bits are good enough for 256 TB.
611 //
612 if (PhysMemAddressWidth <= 36) {
613 PhysMemAddressWidth = 36;
614 }
615
616 ASSERT (PhysMemAddressWidth <= 48);
617
618 PlatformInfoHob->FirstNonAddress = FirstNonAddress;
619 PlatformInfoHob->PhysMemAddressWidth = PhysMemAddressWidth;
620 }
621
622 /**
623 Initialize the PhysMemAddressWidth field in PlatformInfoHob based on guest RAM size.
624 **/
625 VOID
626 AddressWidthInitialization (
627 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
628 )
629 {
630 RETURN_STATUS PcdStatus;
631
632 PlatformAddressWidthInitialization (PlatformInfoHob);
633
634 //
635 // If DXE is 32-bit, then we're done; PciBusDxe will degrade 64-bit MMIO
636 // resources to 32-bit anyway. See DegradeResource() in
637 // "PciResourceSupport.c".
638 //
639 #ifdef MDE_CPU_IA32
640 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
641 return;
642 }
643
644 #endif
645
646 if (PlatformInfoHob->PcdPciMmio64Size == 0) {
647 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
648 DEBUG ((
649 DEBUG_INFO,
650 "%a: disabling 64-bit PCI host aperture\n",
651 __FUNCTION__
652 ));
653 PcdStatus = PcdSet64S (PcdPciMmio64Size, 0);
654 ASSERT_RETURN_ERROR (PcdStatus);
655 }
656
657 return;
658 }
659
660 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
661 //
662 // The core PciHostBridgeDxe driver will automatically add this range to
663 // the GCD memory space map through our PciHostBridgeLib instance; here we
664 // only need to set the PCDs.
665 //
666 PcdStatus = PcdSet64S (PcdPciMmio64Base, PlatformInfoHob->PcdPciMmio64Base);
667 ASSERT_RETURN_ERROR (PcdStatus);
668 PcdStatus = PcdSet64S (PcdPciMmio64Size, PlatformInfoHob->PcdPciMmio64Size);
669 ASSERT_RETURN_ERROR (PcdStatus);
670
671 DEBUG ((
672 DEBUG_INFO,
673 "%a: Pci64Base=0x%Lx Pci64Size=0x%Lx\n",
674 __FUNCTION__,
675 PlatformInfoHob->PcdPciMmio64Base,
676 PlatformInfoHob->PcdPciMmio64Size
677 ));
678 }
679 }
680
681 /**
682 Calculate the cap for the permanent PEI memory.
683 **/
684 STATIC
685 UINT32
686 GetPeiMemoryCap (
687 VOID
688 )
689 {
690 BOOLEAN Page1GSupport;
691 UINT32 RegEax;
692 UINT32 RegEdx;
693 UINT32 Pml4Entries;
694 UINT32 PdpEntries;
695 UINTN TotalPages;
696
697 //
698 // If DXE is 32-bit, then just return the traditional 64 MB cap.
699 //
700 #ifdef MDE_CPU_IA32
701 if (!FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {
702 return SIZE_64MB;
703 }
704
705 #endif
706
707 //
708 // Dependent on physical address width, PEI memory allocations can be
709 // dominated by the page tables built for 64-bit DXE. So we key the cap off
710 // of those. The code below is based on CreateIdentityMappingPageTables() in
711 // "MdeModulePkg/Core/DxeIplPeim/X64/VirtualMemory.c".
712 //
713 Page1GSupport = FALSE;
714 if (PcdGetBool (PcdUse1GPageTable)) {
715 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
716 if (RegEax >= 0x80000001) {
717 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
718 if ((RegEdx & BIT26) != 0) {
719 Page1GSupport = TRUE;
720 }
721 }
722 }
723
724 if (mPlatformInfoHob.PhysMemAddressWidth <= 39) {
725 Pml4Entries = 1;
726 PdpEntries = 1 << (mPlatformInfoHob.PhysMemAddressWidth - 30);
727 ASSERT (PdpEntries <= 0x200);
728 } else {
729 Pml4Entries = 1 << (mPlatformInfoHob.PhysMemAddressWidth - 39);
730 ASSERT (Pml4Entries <= 0x200);
731 PdpEntries = 512;
732 }
733
734 TotalPages = Page1GSupport ? Pml4Entries + 1 :
735 (PdpEntries + 1) * Pml4Entries + 1;
736 ASSERT (TotalPages <= 0x40201);
737
738 //
739 // Add 64 MB for miscellaneous allocations. Note that for
740 // PhysMemAddressWidth values close to 36, the cap will actually be
741 // dominated by this increment.
742 //
743 return (UINT32)(EFI_PAGES_TO_SIZE (TotalPages) + SIZE_64MB);
744 }
745
746 /**
747 Publish PEI core memory
748
749 @return EFI_SUCCESS The PEIM initialized successfully.
750
751 **/
752 EFI_STATUS
753 PublishPeiMemory (
754 VOID
755 )
756 {
757 EFI_STATUS Status;
758 EFI_PHYSICAL_ADDRESS MemoryBase;
759 UINT64 MemorySize;
760 UINT32 LowerMemorySize;
761 UINT32 PeiMemoryCap;
762 UINT32 S3AcpiReservedMemoryBase;
763 UINT32 S3AcpiReservedMemorySize;
764
765 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (&mPlatformInfoHob);
766 if (mPlatformInfoHob.SmmSmramRequire) {
767 //
768 // TSEG is chipped from the end of low RAM
769 //
770 LowerMemorySize -= mPlatformInfoHob.Q35TsegMbytes * SIZE_1MB;
771 }
772
773 S3AcpiReservedMemoryBase = 0;
774 S3AcpiReservedMemorySize = 0;
775
776 //
777 // If S3 is supported, then the S3 permanent PEI memory is placed next,
778 // downwards. Its size is primarily dictated by CpuMpPei. The formula below
779 // is an approximation.
780 //
781 if (mPlatformInfoHob.S3Supported) {
782 S3AcpiReservedMemorySize = SIZE_512KB +
783 mPlatformInfoHob.PcdCpuMaxLogicalProcessorNumber *
784 PcdGet32 (PcdCpuApStackSize);
785 S3AcpiReservedMemoryBase = LowerMemorySize - S3AcpiReservedMemorySize;
786 LowerMemorySize = S3AcpiReservedMemoryBase;
787 }
788
789 mPlatformInfoHob.S3AcpiReservedMemoryBase = S3AcpiReservedMemoryBase;
790 mPlatformInfoHob.S3AcpiReservedMemorySize = S3AcpiReservedMemorySize;
791
792 if (mPlatformInfoHob.BootMode == BOOT_ON_S3_RESUME) {
793 MemoryBase = S3AcpiReservedMemoryBase;
794 MemorySize = S3AcpiReservedMemorySize;
795 } else {
796 PeiMemoryCap = GetPeiMemoryCap ();
797 DEBUG ((
798 DEBUG_INFO,
799 "%a: PhysMemAddressWidth=%d PeiMemoryCap=%u KB\n",
800 __FUNCTION__,
801 mPlatformInfoHob.PhysMemAddressWidth,
802 PeiMemoryCap >> 10
803 ));
804
805 //
806 // Determine the range of memory to use during PEI
807 //
808 // Technically we could lay the permanent PEI RAM over SEC's temporary
809 // decompression and scratch buffer even if "secure S3" is needed, since
810 // their lifetimes don't overlap. However, PeiFvInitialization() will cover
811 // RAM up to PcdOvmfDecompressionScratchEnd with an EfiACPIMemoryNVS memory
812 // allocation HOB, and other allocations served from the permanent PEI RAM
813 // shouldn't overlap with that HOB.
814 //
815 MemoryBase = mPlatformInfoHob.S3Supported && mPlatformInfoHob.SmmSmramRequire ?
816 PcdGet32 (PcdOvmfDecompressionScratchEnd) :
817 PcdGet32 (PcdOvmfDxeMemFvBase) + PcdGet32 (PcdOvmfDxeMemFvSize);
818 MemorySize = LowerMemorySize - MemoryBase;
819 if (MemorySize > PeiMemoryCap) {
820 MemoryBase = LowerMemorySize - PeiMemoryCap;
821 MemorySize = PeiMemoryCap;
822 }
823 }
824
825 //
826 // MEMFD_BASE_ADDRESS separates the SMRAM at the default SMBASE from the
827 // normal boot permanent PEI RAM. Regarding the S3 boot path, the S3
828 // permanent PEI RAM is located even higher.
829 //
830 if (mPlatformInfoHob.SmmSmramRequire && mPlatformInfoHob.Q35SmramAtDefaultSmbase) {
831 ASSERT (SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE <= MemoryBase);
832 }
833
834 //
835 // Publish this memory to the PEI Core
836 //
837 Status = PublishSystemMemory (MemoryBase, MemorySize);
838 ASSERT_EFI_ERROR (Status);
839
840 return Status;
841 }
842
843 STATIC
844 VOID
845 QemuInitializeRamBelow1gb (
846 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
847 )
848 {
849 if (PlatformInfoHob->SmmSmramRequire && PlatformInfoHob->Q35SmramAtDefaultSmbase) {
850 PlatformAddMemoryRangeHob (0, SMM_DEFAULT_SMBASE);
851 PlatformAddReservedMemoryBaseSizeHob (
852 SMM_DEFAULT_SMBASE,
853 MCH_DEFAULT_SMBASE_SIZE,
854 TRUE /* Cacheable */
855 );
856 STATIC_ASSERT (
857 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE < BASE_512KB + BASE_128KB,
858 "end of SMRAM at default SMBASE ends at, or exceeds, 640KB"
859 );
860 PlatformAddMemoryRangeHob (
861 SMM_DEFAULT_SMBASE + MCH_DEFAULT_SMBASE_SIZE,
862 BASE_512KB + BASE_128KB
863 );
864 } else {
865 PlatformAddMemoryRangeHob (0, BASE_512KB + BASE_128KB);
866 }
867 }
868
869 /**
870 Peform Memory Detection for QEMU / KVM
871
872 **/
873 STATIC
874 VOID
875 PlatformQemuInitializeRam (
876 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
877 )
878 {
879 UINT64 LowerMemorySize;
880 UINT64 UpperMemorySize;
881 MTRR_SETTINGS MtrrSettings;
882 EFI_STATUS Status;
883
884 DEBUG ((DEBUG_INFO, "%a called\n", __FUNCTION__));
885
886 //
887 // Determine total memory size available
888 //
889 LowerMemorySize = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
890
891 if (PlatformInfoHob->BootMode == BOOT_ON_S3_RESUME) {
892 //
893 // Create the following memory HOB as an exception on the S3 boot path.
894 //
895 // Normally we'd create memory HOBs only on the normal boot path. However,
896 // CpuMpPei specifically needs such a low-memory HOB on the S3 path as
897 // well, for "borrowing" a subset of it temporarily, for the AP startup
898 // vector.
899 //
900 // CpuMpPei saves the original contents of the borrowed area in permanent
901 // PEI RAM, in a backup buffer allocated with the normal PEI services.
902 // CpuMpPei restores the original contents ("returns" the borrowed area) at
903 // End-of-PEI. End-of-PEI in turn is emitted by S3Resume2Pei before
904 // transferring control to the OS's wakeup vector in the FACS.
905 //
906 // We expect any other PEIMs that "borrow" memory similarly to CpuMpPei to
907 // restore the original contents. Furthermore, we expect all such PEIMs
908 // (CpuMpPei included) to claim the borrowed areas by producing memory
909 // allocation HOBs, and to honor preexistent memory allocation HOBs when
910 // looking for an area to borrow.
911 //
912 QemuInitializeRamBelow1gb (PlatformInfoHob);
913 } else {
914 //
915 // Create memory HOBs
916 //
917 QemuInitializeRamBelow1gb (PlatformInfoHob);
918
919 if (PlatformInfoHob->SmmSmramRequire) {
920 UINT32 TsegSize;
921
922 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
923 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize - TsegSize);
924 PlatformAddReservedMemoryBaseSizeHob (
925 LowerMemorySize - TsegSize,
926 TsegSize,
927 TRUE
928 );
929 } else {
930 PlatformAddMemoryRangeHob (BASE_1MB, LowerMemorySize);
931 }
932
933 //
934 // If QEMU presents an E820 map, then create memory HOBs for the >=4GB RAM
935 // entries. Otherwise, create a single memory HOB with the flat >=4GB
936 // memory size read from the CMOS.
937 //
938 Status = PlatformScanOrAdd64BitE820Ram (TRUE, NULL, NULL);
939 if (EFI_ERROR (Status)) {
940 UpperMemorySize = PlatformGetSystemMemorySizeAbove4gb ();
941 if (UpperMemorySize != 0) {
942 PlatformAddMemoryBaseSizeHob (BASE_4GB, UpperMemorySize);
943 }
944 }
945 }
946
947 //
948 // We'd like to keep the following ranges uncached:
949 // - [640 KB, 1 MB)
950 // - [LowerMemorySize, 4 GB)
951 //
952 // Everything else should be WB. Unfortunately, programming the inverse (ie.
953 // keeping the default UC, and configuring the complement set of the above as
954 // WB) is not reliable in general, because the end of the upper RAM can have
955 // practically any alignment, and we may not have enough variable MTRRs to
956 // cover it exactly.
957 //
958 if (IsMtrrSupported () && (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID)) {
959 MtrrGetAllMtrrs (&MtrrSettings);
960
961 //
962 // MTRRs disabled, fixed MTRRs disabled, default type is uncached
963 //
964 ASSERT ((MtrrSettings.MtrrDefType & BIT11) == 0);
965 ASSERT ((MtrrSettings.MtrrDefType & BIT10) == 0);
966 ASSERT ((MtrrSettings.MtrrDefType & 0xFF) == 0);
967
968 //
969 // flip default type to writeback
970 //
971 SetMem (&MtrrSettings.Fixed, sizeof MtrrSettings.Fixed, 0x06);
972 ZeroMem (&MtrrSettings.Variables, sizeof MtrrSettings.Variables);
973 MtrrSettings.MtrrDefType |= BIT11 | BIT10 | 6;
974 MtrrSetAllMtrrs (&MtrrSettings);
975
976 //
977 // Set memory range from 640KB to 1MB to uncacheable
978 //
979 Status = MtrrSetMemoryAttribute (
980 BASE_512KB + BASE_128KB,
981 BASE_1MB - (BASE_512KB + BASE_128KB),
982 CacheUncacheable
983 );
984 ASSERT_EFI_ERROR (Status);
985
986 //
987 // Set the memory range from the start of the 32-bit MMIO area (32-bit PCI
988 // MMIO aperture on i440fx, PCIEXBAR on q35) to 4GB as uncacheable.
989 //
990 Status = MtrrSetMemoryAttribute (
991 PlatformInfoHob->Uc32Base,
992 SIZE_4GB - PlatformInfoHob->Uc32Base,
993 CacheUncacheable
994 );
995 ASSERT_EFI_ERROR (Status);
996 }
997 }
998
999 STATIC
1000 VOID
1001 PlatformQemuInitializeRamForS3 (
1002 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1003 )
1004 {
1005 if (PlatformInfoHob->S3Supported && (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME)) {
1006 //
1007 // This is the memory range that will be used for PEI on S3 resume
1008 //
1009 BuildMemoryAllocationHob (
1010 PlatformInfoHob->S3AcpiReservedMemoryBase,
1011 PlatformInfoHob->S3AcpiReservedMemorySize,
1012 EfiACPIMemoryNVS
1013 );
1014
1015 //
1016 // Cover the initial RAM area used as stack and temporary PEI heap.
1017 //
1018 // This is reserved as ACPI NVS so it can be used on S3 resume.
1019 //
1020 BuildMemoryAllocationHob (
1021 PcdGet32 (PcdOvmfSecPeiTempRamBase),
1022 PcdGet32 (PcdOvmfSecPeiTempRamSize),
1023 EfiACPIMemoryNVS
1024 );
1025
1026 //
1027 // SEC stores its table of GUIDed section handlers here.
1028 //
1029 BuildMemoryAllocationHob (
1030 PcdGet64 (PcdGuidedExtractHandlerTableAddress),
1031 PcdGet32 (PcdGuidedExtractHandlerTableSize),
1032 EfiACPIMemoryNVS
1033 );
1034
1035 #ifdef MDE_CPU_X64
1036 //
1037 // Reserve the initial page tables built by the reset vector code.
1038 //
1039 // Since this memory range will be used by the Reset Vector on S3
1040 // resume, it must be reserved as ACPI NVS.
1041 //
1042 BuildMemoryAllocationHob (
1043 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecPageTablesBase),
1044 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecPageTablesSize),
1045 EfiACPIMemoryNVS
1046 );
1047
1048 if (PlatformInfoHob->SevEsIsEnabled) {
1049 //
1050 // If SEV-ES is enabled, reserve the GHCB-related memory area. This
1051 // includes the extra page table used to break down the 2MB page
1052 // mapping into 4KB page entries where the GHCB resides and the
1053 // GHCB area itself.
1054 //
1055 // Since this memory range will be used by the Reset Vector on S3
1056 // resume, it must be reserved as ACPI NVS.
1057 //
1058 BuildMemoryAllocationHob (
1059 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableBase),
1060 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbPageTableSize),
1061 EfiACPIMemoryNVS
1062 );
1063 BuildMemoryAllocationHob (
1064 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBase),
1065 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbSize),
1066 EfiACPIMemoryNVS
1067 );
1068 BuildMemoryAllocationHob (
1069 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase),
1070 (UINT64)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupSize),
1071 EfiACPIMemoryNVS
1072 );
1073 }
1074
1075 #endif
1076 }
1077
1078 if (PlatformInfoHob->BootMode != BOOT_ON_S3_RESUME) {
1079 if (!PlatformInfoHob->SmmSmramRequire) {
1080 //
1081 // Reserve the lock box storage area
1082 //
1083 // Since this memory range will be used on S3 resume, it must be
1084 // reserved as ACPI NVS.
1085 //
1086 // If S3 is unsupported, then various drivers might still write to the
1087 // LockBox area. We ought to prevent DXE from serving allocation requests
1088 // such that they would overlap the LockBox storage.
1089 //
1090 ZeroMem (
1091 (VOID *)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1092 (UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize)
1093 );
1094 BuildMemoryAllocationHob (
1095 (EFI_PHYSICAL_ADDRESS)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageBase),
1096 (UINT64)(UINTN)PcdGet32 (PcdOvmfLockBoxStorageSize),
1097 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1098 );
1099 }
1100
1101 if (PlatformInfoHob->SmmSmramRequire) {
1102 UINT32 TsegSize;
1103
1104 //
1105 // Make sure the TSEG area that we reported as a reserved memory resource
1106 // cannot be used for reserved memory allocations.
1107 //
1108 TsegSize = PlatformInfoHob->Q35TsegMbytes * SIZE_1MB;
1109 BuildMemoryAllocationHob (
1110 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob) - TsegSize,
1111 TsegSize,
1112 EfiReservedMemoryType
1113 );
1114 //
1115 // Similarly, allocate away the (already reserved) SMRAM at the default
1116 // SMBASE, if it exists.
1117 //
1118 if (PlatformInfoHob->Q35SmramAtDefaultSmbase) {
1119 BuildMemoryAllocationHob (
1120 SMM_DEFAULT_SMBASE,
1121 MCH_DEFAULT_SMBASE_SIZE,
1122 EfiReservedMemoryType
1123 );
1124 }
1125 }
1126
1127 #ifdef MDE_CPU_X64
1128 if (FixedPcdGet32 (PcdOvmfWorkAreaSize) != 0) {
1129 //
1130 // Reserve the work area.
1131 //
1132 // Since this memory range will be used by the Reset Vector on S3
1133 // resume, it must be reserved as ACPI NVS.
1134 //
1135 // If S3 is unsupported, then various drivers might still write to the
1136 // work area. We ought to prevent DXE from serving allocation requests
1137 // such that they would overlap the work area.
1138 //
1139 BuildMemoryAllocationHob (
1140 (EFI_PHYSICAL_ADDRESS)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaBase),
1141 (UINT64)(UINTN)FixedPcdGet32 (PcdOvmfWorkAreaSize),
1142 PlatformInfoHob->S3Supported ? EfiACPIMemoryNVS : EfiBootServicesData
1143 );
1144 }
1145
1146 #endif
1147 }
1148 }
1149
1150 /**
1151 Publish system RAM and reserve memory regions
1152
1153 **/
1154 VOID
1155 InitializeRamRegions (
1156 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
1157 )
1158 {
1159 PlatformQemuInitializeRam (PlatformInfoHob);
1160
1161 SevInitializeRam ();
1162
1163 PlatformQemuInitializeRamForS3 (PlatformInfoHob);
1164 }