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