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