]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PlatformPei/Platform.c
OvmfPkg/PlatformPei: Refactor MemMapInitialization
[mirror_edk2.git] / OvmfPkg / PlatformPei / Platform.c
1 /**@file
2 Platform PEI driver
3
4 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8
9 **/
10
11 //
12 // The package level header files this module uses
13 //
14 #include <PiPei.h>
15
16 //
17 // The Library classes this module consumes
18 //
19 #include <Library/BaseMemoryLib.h>
20 #include <Library/BaseLib.h>
21 #include <Library/DebugLib.h>
22 #include <Library/HobLib.h>
23 #include <Library/IoLib.h>
24 #include <Library/MemoryAllocationLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/PciLib.h>
27 #include <Library/PeimEntryPoint.h>
28 #include <Library/PeiServicesLib.h>
29 #include <Library/QemuFwCfgLib.h>
30 #include <Library/QemuFwCfgS3Lib.h>
31 #include <Library/QemuFwCfgSimpleParserLib.h>
32 #include <Library/ResourcePublicationLib.h>
33 #include <Ppi/MasterBootMode.h>
34 #include <IndustryStandard/I440FxPiix4.h>
35 #include <IndustryStandard/Microvm.h>
36 #include <IndustryStandard/Pci22.h>
37 #include <IndustryStandard/Q35MchIch9.h>
38 #include <IndustryStandard/QemuCpuHotplug.h>
39 #include <Library/MemEncryptSevLib.h>
40 #include <OvmfPlatforms.h>
41
42 #include "Platform.h"
43
44 EFI_HOB_PLATFORM_INFO mPlatformInfoHob = { 0 };
45
46 EFI_PEI_PPI_DESCRIPTOR mPpiBootMode[] = {
47 {
48 EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
49 &gEfiPeiMasterBootModePpiGuid,
50 NULL
51 }
52 };
53
54 VOID
55 EFIAPI
56 PlatformMemMapInitialization (
57 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
58 )
59 {
60 UINT64 PciIoBase;
61 UINT64 PciIoSize;
62 RETURN_STATUS PcdStatus;
63 UINT32 TopOfLowRam;
64 UINT64 PciExBarBase;
65 UINT32 PciBase;
66 UINT32 PciSize;
67
68 PciIoBase = 0xC000;
69 PciIoSize = 0x4000;
70
71 //
72 // Video memory + Legacy BIOS region
73 //
74 PlatformAddIoMemoryRangeHob (0x0A0000, BASE_1MB);
75
76 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
77 PlatformAddIoMemoryBaseSizeHob (MICROVM_GED_MMIO_BASE, SIZE_4KB);
78 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB); /* ioapic #1 */
79 PlatformAddIoMemoryBaseSizeHob (0xFEC10000, SIZE_4KB); /* ioapic #2 */
80 return;
81 }
82
83 TopOfLowRam = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);
84 PciExBarBase = 0;
85 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
86 //
87 // The MMCONFIG area is expected to fall between the top of low RAM and
88 // the base of the 32-bit PCI host aperture.
89 //
90 PciExBarBase = FixedPcdGet64 (PcdPciExpressBaseAddress);
91 ASSERT (TopOfLowRam <= PciExBarBase);
92 ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB);
93 PciBase = (UINT32)(PciExBarBase + SIZE_256MB);
94 } else {
95 ASSERT (TopOfLowRam <= PlatformInfoHob->Uc32Base);
96 PciBase = PlatformInfoHob->Uc32Base;
97 }
98
99 //
100 // address purpose size
101 // ------------ -------- -------------------------
102 // max(top, 2g) PCI MMIO 0xFC000000 - max(top, 2g)
103 // 0xFC000000 gap 44 MB
104 // 0xFEC00000 IO-APIC 4 KB
105 // 0xFEC01000 gap 1020 KB
106 // 0xFED00000 HPET 1 KB
107 // 0xFED00400 gap 111 KB
108 // 0xFED1C000 gap (PIIX4) / RCRB (ICH9) 16 KB
109 // 0xFED20000 gap 896 KB
110 // 0xFEE00000 LAPIC 1 MB
111 //
112 PciSize = 0xFC000000 - PciBase;
113 PlatformAddIoMemoryBaseSizeHob (PciBase, PciSize);
114
115 PlatformInfoHob->PcdPciMmio32Base = PciBase;
116 PlatformInfoHob->PcdPciMmio32Size = PciSize;
117
118 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB);
119 PlatformAddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB);
120 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
121 PlatformAddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB);
122 //
123 // Note: there should be an
124 //
125 // PlatformAddIoMemoryBaseSizeHob (PciExBarBase, SIZE_256MB);
126 //
127 // call below, just like the one above for RCBA. However, Linux insists
128 // that the MMCONFIG area be marked in the E820 or UEFI memory map as
129 // "reserved memory" -- Linux does not content itself with a simple gap
130 // in the memory map wherever the MCFG ACPI table points to.
131 //
132 // This appears to be a safety measure. The PCI Firmware Specification
133 // (rev 3.1) says in 4.1.2. "MCFG Table Description": "The resources can
134 // *optionally* be returned in [...] EFIGetMemoryMap as reserved memory
135 // [...]". (Emphasis added here.)
136 //
137 // Normally we add memory resource descriptor HOBs in
138 // QemuInitializeRam(), and pre-allocate from those with memory
139 // allocation HOBs in InitializeRamRegions(). However, the MMCONFIG area
140 // is most definitely not RAM; so, as an exception, cover it with
141 // uncacheable reserved memory right here.
142 //
143 PlatformAddReservedMemoryBaseSizeHob (PciExBarBase, SIZE_256MB, FALSE);
144 BuildMemoryAllocationHob (
145 PciExBarBase,
146 SIZE_256MB,
147 EfiReservedMemoryType
148 );
149 }
150
151 PlatformAddIoMemoryBaseSizeHob (PcdGet32 (PcdCpuLocalApicBaseAddress), SIZE_1MB);
152
153 //
154 // On Q35, the IO Port space is available for PCI resource allocations from
155 // 0x6000 up.
156 //
157 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
158 PciIoBase = 0x6000;
159 PciIoSize = 0xA000;
160 ASSERT ((ICH9_PMBASE_VALUE & 0xF000) < PciIoBase);
161 }
162
163 //
164 // Add PCI IO Port space available for PCI resource allocations.
165 //
166 BuildResourceDescriptorHob (
167 EFI_RESOURCE_IO,
168 EFI_RESOURCE_ATTRIBUTE_PRESENT |
169 EFI_RESOURCE_ATTRIBUTE_INITIALIZED,
170 PciIoBase,
171 PciIoSize
172 );
173
174 PlatformInfoHob->PcdPciIoBase = PciIoBase;
175 PlatformInfoHob->PcdPciIoSize = PciIoSize;
176 }
177
178 VOID
179 MemMapInitialization (
180 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
181 )
182 {
183 RETURN_STATUS PcdStatus;
184
185 PlatformMemMapInitialization (PlatformInfoHob);
186
187 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {
188 return;
189 }
190
191 PcdStatus = PcdSet64S (PcdPciMmio32Base, PlatformInfoHob->PcdPciMmio32Base);
192 ASSERT_RETURN_ERROR (PcdStatus);
193 PcdStatus = PcdSet64S (PcdPciMmio32Size, PlatformInfoHob->PcdPciMmio32Size);
194 ASSERT_RETURN_ERROR (PcdStatus);
195
196 PcdStatus = PcdSet64S (PcdPciIoBase, PlatformInfoHob->PcdPciIoBase);
197 ASSERT_RETURN_ERROR (PcdStatus);
198 PcdStatus = PcdSet64S (PcdPciIoSize, PlatformInfoHob->PcdPciIoSize);
199 ASSERT_RETURN_ERROR (PcdStatus);
200 }
201
202 #define UPDATE_BOOLEAN_PCD_FROM_FW_CFG(TokenName) \
203 do { \
204 BOOLEAN Setting; \
205 RETURN_STATUS PcdStatus; \
206 \
207 if (!RETURN_ERROR (QemuFwCfgParseBool ( \
208 "opt/ovmf/" #TokenName, &Setting))) { \
209 PcdStatus = PcdSetBoolS (TokenName, Setting); \
210 ASSERT_RETURN_ERROR (PcdStatus); \
211 } \
212 } while (0)
213
214 VOID
215 NoexecDxeInitialization (
216 VOID
217 )
218 {
219 UPDATE_BOOLEAN_PCD_FROM_FW_CFG (PcdSetNxForStack);
220 }
221
222 VOID
223 PciExBarInitialization (
224 VOID
225 )
226 {
227 union {
228 UINT64 Uint64;
229 UINT32 Uint32[2];
230 } PciExBarBase;
231
232 //
233 // We only support the 256MB size for the MMCONFIG area:
234 // 256 buses * 32 devices * 8 functions * 4096 bytes config space.
235 //
236 // The masks used below enforce the Q35 requirements that the MMCONFIG area
237 // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB.
238 //
239 // Note that (b) also ensures that the minimum address width we have
240 // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice
241 // for DXE's page tables to cover the MMCONFIG area.
242 //
243 PciExBarBase.Uint64 = FixedPcdGet64 (PcdPciExpressBaseAddress);
244 ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0);
245 ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0);
246
247 //
248 // Clear the PCIEXBAREN bit first, before programming the high register.
249 //
250 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0);
251
252 //
253 // Program the high register. Then program the low register, setting the
254 // MMCONFIG area size and enabling decoding at once.
255 //
256 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]);
257 PciWrite32 (
258 DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW),
259 PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN
260 );
261 }
262
263 static const UINT8 EmptyFdt[] = {
264 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x48,
265 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x48,
266 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
267 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
271 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
272 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
273 };
274
275 VOID
276 MicrovmInitialization (
277 VOID
278 )
279 {
280 FIRMWARE_CONFIG_ITEM FdtItem;
281 UINTN FdtSize;
282 UINTN FdtPages;
283 EFI_STATUS Status;
284 UINT64 *FdtHobData;
285 VOID *NewBase;
286
287 Status = QemuFwCfgFindFile ("etc/fdt", &FdtItem, &FdtSize);
288 if (EFI_ERROR (Status)) {
289 DEBUG ((DEBUG_INFO, "%a: no etc/fdt found in fw_cfg, using dummy\n", __FUNCTION__));
290 FdtItem = 0;
291 FdtSize = sizeof (EmptyFdt);
292 }
293
294 FdtPages = EFI_SIZE_TO_PAGES (FdtSize);
295 NewBase = AllocatePages (FdtPages);
296 if (NewBase == NULL) {
297 DEBUG ((DEBUG_INFO, "%a: AllocatePages failed\n", __FUNCTION__));
298 return;
299 }
300
301 if (FdtItem) {
302 QemuFwCfgSelectItem (FdtItem);
303 QemuFwCfgReadBytes (FdtSize, NewBase);
304 } else {
305 CopyMem (NewBase, EmptyFdt, FdtSize);
306 }
307
308 FdtHobData = BuildGuidHob (&gFdtHobGuid, sizeof (*FdtHobData));
309 if (FdtHobData == NULL) {
310 DEBUG ((DEBUG_INFO, "%a: BuildGuidHob failed\n", __FUNCTION__));
311 return;
312 }
313
314 DEBUG ((
315 DEBUG_INFO,
316 "%a: fdt at 0x%x (size %d)\n",
317 __FUNCTION__,
318 NewBase,
319 FdtSize
320 ));
321 *FdtHobData = (UINTN)NewBase;
322 }
323
324 VOID
325 MiscInitializationForMicrovm (
326 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
327 )
328 {
329 RETURN_STATUS PcdStatus;
330
331 ASSERT (PlatformInfoHob->HostBridgeDevId == 0xffff);
332
333 DEBUG ((DEBUG_INFO, "%a: microvm\n", __FUNCTION__));
334 //
335 // Disable A20 Mask
336 //
337 IoOr8 (0x92, BIT1);
338
339 //
340 // Build the CPU HOB with guest RAM size dependent address width and 16-bits
341 // of IO space. (Side note: unlike other HOBs, the CPU HOB is needed during
342 // S3 resume as well, so we build it unconditionally.)
343 //
344 BuildCpuHob (PlatformInfoHob->PhysMemAddressWidth, 16);
345
346 MicrovmInitialization ();
347 PcdStatus = PcdSet16S (
348 PcdOvmfHostBridgePciDevId,
349 MICROVM_PSEUDO_DEVICE_ID
350 );
351 ASSERT_RETURN_ERROR (PcdStatus);
352 }
353
354 VOID
355 MiscInitialization (
356 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob
357 )
358 {
359 UINTN PmCmd;
360 UINTN Pmba;
361 UINT32 PmbaAndVal;
362 UINT32 PmbaOrVal;
363 UINTN AcpiCtlReg;
364 UINT8 AcpiEnBit;
365 RETURN_STATUS PcdStatus;
366
367 //
368 // Disable A20 Mask
369 //
370 IoOr8 (0x92, BIT1);
371
372 //
373 // Build the CPU HOB with guest RAM size dependent address width and 16-bits
374 // of IO space. (Side note: unlike other HOBs, the CPU HOB is needed during
375 // S3 resume as well, so we build it unconditionally.)
376 //
377 BuildCpuHob (PlatformInfoHob->PhysMemAddressWidth, 16);
378
379 //
380 // Determine platform type and save Host Bridge DID to PCD
381 //
382 switch (PlatformInfoHob->HostBridgeDevId) {
383 case INTEL_82441_DEVICE_ID:
384 PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET);
385 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
386 PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;
387 PmbaOrVal = PIIX4_PMBA_VALUE;
388 AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);
389 AcpiEnBit = PIIX4_PMREGMISC_PMIOSE;
390 break;
391 case INTEL_Q35_MCH_DEVICE_ID:
392 PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET);
393 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
394 PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;
395 PmbaOrVal = ICH9_PMBASE_VALUE;
396 AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);
397 AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN;
398 break;
399 case CLOUDHV_DEVICE_ID:
400 break;
401 default:
402 DEBUG ((
403 DEBUG_ERROR,
404 "%a: Unknown Host Bridge Device ID: 0x%04x\n",
405 __FUNCTION__,
406 PlatformInfoHob->HostBridgeDevId
407 ));
408 ASSERT (FALSE);
409 return;
410 }
411
412 PcdStatus = PcdSet16S (PcdOvmfHostBridgePciDevId, PlatformInfoHob->HostBridgeDevId);
413 ASSERT_RETURN_ERROR (PcdStatus);
414
415 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {
416 DEBUG ((DEBUG_INFO, "%a: Cloud Hypervisor is done.\n", __FUNCTION__));
417 return;
418 }
419
420 //
421 // If the appropriate IOspace enable bit is set, assume the ACPI PMBA has
422 // been configured and skip the setup here. This matches the logic in
423 // AcpiTimerLibConstructor ().
424 //
425 if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {
426 //
427 // The PEI phase should be exited with fully accessibe ACPI PM IO space:
428 // 1. set PMBA
429 //
430 PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);
431
432 //
433 // 2. set PCICMD/IOSE
434 //
435 PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE);
436
437 //
438 // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN)
439 //
440 PciOr8 (AcpiCtlReg, AcpiEnBit);
441 }
442
443 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
444 //
445 // Set Root Complex Register Block BAR
446 //
447 PciWrite32 (
448 POWER_MGMT_REGISTER_Q35 (ICH9_RCBA),
449 ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN
450 );
451
452 //
453 // Set PCI Express Register Range Base Address
454 //
455 PciExBarInitialization ();
456 }
457 }
458
459 VOID
460 BootModeInitialization (
461 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
462 )
463 {
464 EFI_STATUS Status;
465
466 if (PlatformCmosRead8 (0xF) == 0xFE) {
467 PlatformInfoHob->BootMode = BOOT_ON_S3_RESUME;
468 }
469
470 PlatformCmosWrite8 (0xF, 0x00);
471
472 Status = PeiServicesSetBootMode (PlatformInfoHob->BootMode);
473 ASSERT_EFI_ERROR (Status);
474
475 Status = PeiServicesInstallPpi (mPpiBootMode);
476 ASSERT_EFI_ERROR (Status);
477 }
478
479 VOID
480 ReserveEmuVariableNvStore (
481 )
482 {
483 EFI_PHYSICAL_ADDRESS VariableStore;
484 RETURN_STATUS PcdStatus;
485
486 //
487 // Allocate storage for NV variables early on so it will be
488 // at a consistent address. Since VM memory is preserved
489 // across reboots, this allows the NV variable storage to survive
490 // a VM reboot.
491 //
492 VariableStore =
493 (EFI_PHYSICAL_ADDRESS)(UINTN)
494 AllocateRuntimePages (
495 EFI_SIZE_TO_PAGES (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize))
496 );
497 DEBUG ((
498 DEBUG_INFO,
499 "Reserved variable store memory: 0x%lX; size: %dkb\n",
500 VariableStore,
501 (2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize)) / 1024
502 ));
503 PcdStatus = PcdSet64S (PcdEmuVariableNvStoreReserved, VariableStore);
504 ASSERT_RETURN_ERROR (PcdStatus);
505 }
506
507 VOID
508 S3Verification (
509 VOID
510 )
511 {
512 #if defined (MDE_CPU_X64)
513 if (mPlatformInfoHob.SmmSmramRequire && mPlatformInfoHob.S3Supported) {
514 DEBUG ((
515 DEBUG_ERROR,
516 "%a: S3Resume2Pei doesn't support X64 PEI + SMM yet.\n",
517 __FUNCTION__
518 ));
519 DEBUG ((
520 DEBUG_ERROR,
521 "%a: Please disable S3 on the QEMU command line (see the README),\n",
522 __FUNCTION__
523 ));
524 DEBUG ((
525 DEBUG_ERROR,
526 "%a: or build OVMF with \"OvmfPkgIa32X64.dsc\".\n",
527 __FUNCTION__
528 ));
529 ASSERT (FALSE);
530 CpuDeadLoop ();
531 }
532
533 #endif
534 }
535
536 VOID
537 Q35BoardVerification (
538 VOID
539 )
540 {
541 if (mPlatformInfoHob.HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {
542 return;
543 }
544
545 DEBUG ((
546 DEBUG_ERROR,
547 "%a: no TSEG (SMRAM) on host bridge DID=0x%04x; "
548 "only DID=0x%04x (Q35) is supported\n",
549 __FUNCTION__,
550 mPlatformInfoHob.HostBridgeDevId,
551 INTEL_Q35_MCH_DEVICE_ID
552 ));
553 ASSERT (FALSE);
554 CpuDeadLoop ();
555 }
556
557 /**
558 Fetch the boot CPU count and the possible CPU count from QEMU, and expose
559 them to UefiCpuPkg modules. Set the MaxCpuCount field in PlatformInfoHob.
560 **/
561 VOID
562 PlatformMaxCpuCountInitialization (
563 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
564 )
565 {
566 UINT16 BootCpuCount;
567 UINT32 MaxCpuCount;
568
569 //
570 // Try to fetch the boot CPU count.
571 //
572 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);
573 BootCpuCount = QemuFwCfgRead16 ();
574 if (BootCpuCount == 0) {
575 //
576 // QEMU doesn't report the boot CPU count. (BootCpuCount == 0) will let
577 // MpInitLib count APs up to (PcdCpuMaxLogicalProcessorNumber - 1), or
578 // until PcdCpuApInitTimeOutInMicroSeconds elapses (whichever is reached
579 // first).
580 //
581 DEBUG ((DEBUG_WARN, "%a: boot CPU count unavailable\n", __FUNCTION__));
582 MaxCpuCount = PlatformInfoHob->DefaultMaxCpuNumber;
583 } else {
584 //
585 // We will expose BootCpuCount to MpInitLib. MpInitLib will count APs up to
586 // (BootCpuCount - 1) precisely, regardless of timeout.
587 //
588 // Now try to fetch the possible CPU count.
589 //
590 UINTN CpuHpBase;
591 UINT32 CmdData2;
592
593 CpuHpBase = ((PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) ?
594 ICH9_CPU_HOTPLUG_BASE : PIIX4_CPU_HOTPLUG_BASE);
595
596 //
597 // If only legacy mode is available in the CPU hotplug register block, or
598 // the register block is completely missing, then the writes below are
599 // no-ops.
600 //
601 // 1. Switch the hotplug register block to modern mode.
602 //
603 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);
604 //
605 // 2. Select a valid CPU for deterministic reading of
606 // QEMU_CPUHP_R_CMD_DATA2.
607 //
608 // CPU#0 is always valid; it is the always present and non-removable
609 // BSP.
610 //
611 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);
612 //
613 // 3. Send a command after which QEMU_CPUHP_R_CMD_DATA2 is specified to
614 // read as zero, and which does not invalidate the selector. (The
615 // selector may change, but it must not become invalid.)
616 //
617 // Send QEMU_CPUHP_CMD_GET_PENDING, as it will prove useful later.
618 //
619 IoWrite8 (CpuHpBase + QEMU_CPUHP_W_CMD, QEMU_CPUHP_CMD_GET_PENDING);
620 //
621 // 4. Read QEMU_CPUHP_R_CMD_DATA2.
622 //
623 // If the register block is entirely missing, then this is an unassigned
624 // IO read, returning all-bits-one.
625 //
626 // If only legacy mode is available, then bit#0 stands for CPU#0 in the
627 // "CPU present bitmap". CPU#0 is always present.
628 //
629 // Otherwise, QEMU_CPUHP_R_CMD_DATA2 is either still reserved (returning
630 // all-bits-zero), or it is specified to read as zero after the above
631 // steps. Both cases confirm modern mode.
632 //
633 CmdData2 = IoRead32 (CpuHpBase + QEMU_CPUHP_R_CMD_DATA2);
634 DEBUG ((DEBUG_VERBOSE, "%a: CmdData2=0x%x\n", __FUNCTION__, CmdData2));
635 if (CmdData2 != 0) {
636 //
637 // QEMU doesn't support the modern CPU hotplug interface. Assume that the
638 // possible CPU count equals the boot CPU count (precluding hotplug).
639 //
640 DEBUG ((
641 DEBUG_WARN,
642 "%a: modern CPU hotplug interface unavailable\n",
643 __FUNCTION__
644 ));
645 MaxCpuCount = BootCpuCount;
646 } else {
647 //
648 // Grab the possible CPU count from the modern CPU hotplug interface.
649 //
650 UINT32 Present, Possible, Selected;
651
652 Present = 0;
653 Possible = 0;
654
655 //
656 // We've sent QEMU_CPUHP_CMD_GET_PENDING last; this ensures
657 // QEMU_CPUHP_RW_CMD_DATA can now be read usefully. However,
658 // QEMU_CPUHP_CMD_GET_PENDING may have selected a CPU with actual pending
659 // hotplug events; therefore, select CPU#0 forcibly.
660 //
661 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);
662
663 do {
664 UINT8 CpuStatus;
665
666 //
667 // Read the status of the currently selected CPU. This will help with a
668 // sanity check against "BootCpuCount".
669 //
670 CpuStatus = IoRead8 (CpuHpBase + QEMU_CPUHP_R_CPU_STAT);
671 if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) != 0) {
672 ++Present;
673 }
674
675 //
676 // Attempt to select the next CPU.
677 //
678 ++Possible;
679 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);
680 //
681 // If the selection is successful, then the following read will return
682 // the selector (which we know is positive at this point). Otherwise,
683 // the read will return 0.
684 //
685 Selected = IoRead32 (CpuHpBase + QEMU_CPUHP_RW_CMD_DATA);
686 ASSERT (Selected == Possible || Selected == 0);
687 } while (Selected > 0);
688
689 //
690 // Sanity check: fw_cfg and the modern CPU hotplug interface should
691 // return the same boot CPU count.
692 //
693 if (BootCpuCount != Present) {
694 DEBUG ((
695 DEBUG_WARN,
696 "%a: QEMU v2.7 reset bug: BootCpuCount=%d "
697 "Present=%u\n",
698 __FUNCTION__,
699 BootCpuCount,
700 Present
701 ));
702 //
703 // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus
704 // platform reset (including S3), was corrected in QEMU commit
705 // e3cadac073a9 ("pc: fix FW_CFG_NB_CPUS to account for -device added
706 // CPUs", 2016-11-16), part of release v2.8.0.
707 //
708 BootCpuCount = (UINT16)Present;
709 }
710
711 MaxCpuCount = Possible;
712 }
713 }
714
715 DEBUG ((
716 DEBUG_INFO,
717 "%a: BootCpuCount=%d MaxCpuCount=%u\n",
718 __FUNCTION__,
719 BootCpuCount,
720 MaxCpuCount
721 ));
722 ASSERT (BootCpuCount <= MaxCpuCount);
723
724 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber = MaxCpuCount;
725 PlatformInfoHob->PcdCpuBootLogicalProcessorNumber = BootCpuCount;
726 }
727
728 /**
729 Fetch the boot CPU count and the possible CPU count from QEMU, and expose
730 them to UefiCpuPkg modules. Set the MaxCpuCount field in PlatformInfoHob.
731 **/
732 VOID
733 MaxCpuCountInitialization (
734 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
735 )
736 {
737 RETURN_STATUS PcdStatus;
738
739 PlatformMaxCpuCountInitialization (PlatformInfoHob);
740
741 PcdStatus = PcdSet32S (PcdCpuBootLogicalProcessorNumber, PlatformInfoHob->PcdCpuBootLogicalProcessorNumber);
742 ASSERT_RETURN_ERROR (PcdStatus);
743 PcdStatus = PcdSet32S (PcdCpuMaxLogicalProcessorNumber, PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber);
744 ASSERT_RETURN_ERROR (PcdStatus);
745 }
746
747 /**
748 Perform Platform PEI initialization.
749
750 @param FileHandle Handle of the file being invoked.
751 @param PeiServices Describes the list of possible PEI Services.
752
753 @return EFI_SUCCESS The PEIM initialized successfully.
754
755 **/
756 EFI_STATUS
757 EFIAPI
758 InitializePlatform (
759 IN EFI_PEI_FILE_HANDLE FileHandle,
760 IN CONST EFI_PEI_SERVICES **PeiServices
761 )
762 {
763 EFI_STATUS Status;
764
765 DEBUG ((DEBUG_INFO, "Platform PEIM Loaded\n"));
766
767 mPlatformInfoHob.SmmSmramRequire = FeaturePcdGet (PcdSmmSmramRequire);
768 mPlatformInfoHob.SevEsIsEnabled = MemEncryptSevEsIsEnabled ();
769 mPlatformInfoHob.PcdPciMmio64Size = PcdGet64 (PcdPciMmio64Size);
770 mPlatformInfoHob.DefaultMaxCpuNumber = PcdGet32 (PcdCpuMaxLogicalProcessorNumber);
771
772 PlatformDebugDumpCmos ();
773
774 if (QemuFwCfgS3Enabled ()) {
775 DEBUG ((DEBUG_INFO, "S3 support was detected on QEMU\n"));
776 mPlatformInfoHob.S3Supported = TRUE;
777 Status = PcdSetBoolS (PcdAcpiS3Enable, TRUE);
778 ASSERT_EFI_ERROR (Status);
779 }
780
781 S3Verification ();
782 BootModeInitialization (&mPlatformInfoHob);
783 AddressWidthInitialization (&mPlatformInfoHob);
784
785 //
786 // Query Host Bridge DID
787 //
788 mPlatformInfoHob.HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
789
790 MaxCpuCountInitialization (&mPlatformInfoHob);
791
792 if (mPlatformInfoHob.SmmSmramRequire) {
793 Q35BoardVerification ();
794 Q35TsegMbytesInitialization ();
795 Q35SmramAtDefaultSmbaseInitialization ();
796 }
797
798 PublishPeiMemory ();
799
800 PlatformQemuUc32BaseInitialization (&mPlatformInfoHob);
801
802 InitializeRamRegions (&mPlatformInfoHob);
803
804 if (mPlatformInfoHob.BootMode != BOOT_ON_S3_RESUME) {
805 if (!mPlatformInfoHob.SmmSmramRequire) {
806 ReserveEmuVariableNvStore ();
807 }
808
809 PeiFvInitialization ();
810 MemTypeInfoInitialization ();
811 MemMapInitialization (&mPlatformInfoHob);
812 NoexecDxeInitialization ();
813 }
814
815 InstallClearCacheCallback ();
816 AmdSevInitialize ();
817 if (mPlatformInfoHob.HostBridgeDevId == 0xffff) {
818 MiscInitializationForMicrovm (&mPlatformInfoHob);
819 } else {
820 MiscInitialization (&mPlatformInfoHob);
821 }
822
823 InstallFeatureControlCallback ();
824
825 return EFI_SUCCESS;
826 }