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