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