]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformInitLib/Platform.c
OvmfPkg: CloudHv: Fix FW_BASE_ADDRESS
[mirror_edk2.git] / OvmfPkg / Library / PlatformInitLib / Platform.c
CommitLineData
102cafed
MX
1/**@file\r
2\r
3 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
4 Copyright (c) 2011, Andrei Warkentin <andreiw@motorola.com>\r
5\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10//\r
11// The package level header files this module uses\r
12//\r
13#include <PiPei.h>\r
14\r
15//\r
16// The Library classes this module consumes\r
17//\r
18#include <Library/BaseMemoryLib.h>\r
19#include <Library/BaseLib.h>\r
20#include <Library/DebugLib.h>\r
21#include <Library/HobLib.h>\r
96047b66
MX
22#include <Library/IoLib.h>\r
23#include <IndustryStandard/I440FxPiix4.h>\r
24#include <IndustryStandard/Microvm.h>\r
25#include <IndustryStandard/Pci22.h>\r
26#include <IndustryStandard/Q35MchIch9.h>\r
27#include <IndustryStandard/QemuCpuHotplug.h>\r
28#include <Library/QemuFwCfgLib.h>\r
29#include <Library/QemuFwCfgS3Lib.h>\r
30#include <Library/QemuFwCfgSimpleParserLib.h>\r
31#include <Library/PciLib.h>\r
32#include <OvmfPlatforms.h>\r
33\r
102cafed
MX
34#include <Library/PlatformInitLib.h>\r
35\r
36VOID\r
37EFIAPI\r
38PlatformAddIoMemoryBaseSizeHob (\r
39 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
40 IN UINT64 MemorySize\r
41 )\r
42{\r
43 BuildResourceDescriptorHob (\r
44 EFI_RESOURCE_MEMORY_MAPPED_IO,\r
45 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
46 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
47 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
48 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
49 MemoryBase,\r
50 MemorySize\r
51 );\r
52}\r
53\r
54VOID\r
55EFIAPI\r
56PlatformAddReservedMemoryBaseSizeHob (\r
57 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
58 IN UINT64 MemorySize,\r
59 IN BOOLEAN Cacheable\r
60 )\r
61{\r
62 BuildResourceDescriptorHob (\r
63 EFI_RESOURCE_MEMORY_RESERVED,\r
64 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
65 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
66 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
67 (Cacheable ?\r
68 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
69 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
70 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE :\r
71 0\r
72 ) |\r
73 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
74 MemoryBase,\r
75 MemorySize\r
76 );\r
77}\r
78\r
79VOID\r
80EFIAPI\r
81PlatformAddIoMemoryRangeHob (\r
82 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
83 IN EFI_PHYSICAL_ADDRESS MemoryLimit\r
84 )\r
85{\r
86 PlatformAddIoMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));\r
87}\r
88\r
89VOID\r
90EFIAPI\r
91PlatformAddMemoryBaseSizeHob (\r
92 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
93 IN UINT64 MemorySize\r
94 )\r
95{\r
96 BuildResourceDescriptorHob (\r
97 EFI_RESOURCE_SYSTEM_MEMORY,\r
98 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
99 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
100 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
101 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
102 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
103 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |\r
104 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
105 MemoryBase,\r
106 MemorySize\r
107 );\r
108}\r
109\r
110VOID\r
111EFIAPI\r
112PlatformAddMemoryRangeHob (\r
113 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
114 IN EFI_PHYSICAL_ADDRESS MemoryLimit\r
115 )\r
116{\r
117 PlatformAddMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));\r
118}\r
96047b66
MX
119\r
120VOID\r
121EFIAPI\r
122PlatformMemMapInitialization (\r
123 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
124 )\r
125{\r
126 UINT64 PciIoBase;\r
127 UINT64 PciIoSize;\r
128 UINT32 TopOfLowRam;\r
129 UINT64 PciExBarBase;\r
130 UINT32 PciBase;\r
131 UINT32 PciSize;\r
132\r
133 PciIoBase = 0xC000;\r
134 PciIoSize = 0x4000;\r
135\r
136 //\r
137 // Video memory + Legacy BIOS region\r
138 //\r
e23f8f52
MX
139 if (!TdIsEnabled ()) {\r
140 PlatformAddIoMemoryRangeHob (0x0A0000, BASE_1MB);\r
141 }\r
96047b66
MX
142\r
143 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
144 PlatformAddIoMemoryBaseSizeHob (MICROVM_GED_MMIO_BASE, SIZE_4KB);\r
145 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB); /* ioapic #1 */\r
146 PlatformAddIoMemoryBaseSizeHob (0xFEC10000, SIZE_4KB); /* ioapic #2 */\r
147 return;\r
148 }\r
149\r
150 TopOfLowRam = PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
151 PciExBarBase = 0;\r
152 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
153 //\r
154 // The MMCONFIG area is expected to fall between the top of low RAM and\r
155 // the base of the 32-bit PCI host aperture.\r
156 //\r
47f44097 157 PciExBarBase = PcdGet64 (PcdPciExpressBaseAddress);\r
96047b66
MX
158 ASSERT (TopOfLowRam <= PciExBarBase);\r
159 ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB);\r
160 PciBase = (UINT32)(PciExBarBase + SIZE_256MB);\r
161 } else {\r
162 ASSERT (TopOfLowRam <= PlatformInfoHob->Uc32Base);\r
163 PciBase = PlatformInfoHob->Uc32Base;\r
164 }\r
165\r
166 //\r
167 // address purpose size\r
168 // ------------ -------- -------------------------\r
169 // max(top, 2g) PCI MMIO 0xFC000000 - max(top, 2g)\r
170 // 0xFC000000 gap 44 MB\r
171 // 0xFEC00000 IO-APIC 4 KB\r
172 // 0xFEC01000 gap 1020 KB\r
173 // 0xFED00000 HPET 1 KB\r
174 // 0xFED00400 gap 111 KB\r
175 // 0xFED1C000 gap (PIIX4) / RCRB (ICH9) 16 KB\r
176 // 0xFED20000 gap 896 KB\r
177 // 0xFEE00000 LAPIC 1 MB\r
178 //\r
179 PciSize = 0xFC000000 - PciBase;\r
180 PlatformAddIoMemoryBaseSizeHob (PciBase, PciSize);\r
181\r
182 PlatformInfoHob->PcdPciMmio32Base = PciBase;\r
183 PlatformInfoHob->PcdPciMmio32Size = PciSize;\r
184\r
185 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB);\r
186 PlatformAddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB);\r
187 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
188 PlatformAddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB);\r
189 //\r
190 // Note: there should be an\r
191 //\r
192 // PlatformAddIoMemoryBaseSizeHob (PciExBarBase, SIZE_256MB);\r
193 //\r
194 // call below, just like the one above for RCBA. However, Linux insists\r
195 // that the MMCONFIG area be marked in the E820 or UEFI memory map as\r
196 // "reserved memory" -- Linux does not content itself with a simple gap\r
197 // in the memory map wherever the MCFG ACPI table points to.\r
198 //\r
199 // This appears to be a safety measure. The PCI Firmware Specification\r
200 // (rev 3.1) says in 4.1.2. "MCFG Table Description": "The resources can\r
201 // *optionally* be returned in [...] EFIGetMemoryMap as reserved memory\r
202 // [...]". (Emphasis added here.)\r
203 //\r
204 // Normally we add memory resource descriptor HOBs in\r
205 // QemuInitializeRam(), and pre-allocate from those with memory\r
206 // allocation HOBs in InitializeRamRegions(). However, the MMCONFIG area\r
207 // is most definitely not RAM; so, as an exception, cover it with\r
208 // uncacheable reserved memory right here.\r
209 //\r
210 PlatformAddReservedMemoryBaseSizeHob (PciExBarBase, SIZE_256MB, FALSE);\r
211 BuildMemoryAllocationHob (\r
212 PciExBarBase,\r
213 SIZE_256MB,\r
214 EfiReservedMemoryType\r
215 );\r
216 }\r
217\r
218 PlatformAddIoMemoryBaseSizeHob (PcdGet32 (PcdCpuLocalApicBaseAddress), SIZE_1MB);\r
219\r
220 //\r
221 // On Q35, the IO Port space is available for PCI resource allocations from\r
222 // 0x6000 up.\r
223 //\r
224 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
225 PciIoBase = 0x6000;\r
226 PciIoSize = 0xA000;\r
227 ASSERT ((ICH9_PMBASE_VALUE & 0xF000) < PciIoBase);\r
228 }\r
229\r
230 //\r
231 // Add PCI IO Port space available for PCI resource allocations.\r
232 //\r
233 BuildResourceDescriptorHob (\r
234 EFI_RESOURCE_IO,\r
235 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
236 EFI_RESOURCE_ATTRIBUTE_INITIALIZED,\r
237 PciIoBase,\r
238 PciIoSize\r
239 );\r
240\r
241 PlatformInfoHob->PcdPciIoBase = PciIoBase;\r
242 PlatformInfoHob->PcdPciIoSize = PciIoSize;\r
243}\r
244\r
245/**\r
246 * Fetch "opt/ovmf/PcdSetNxForStack" from QEMU\r
247 *\r
248 * @param Setting The pointer to the setting of "/opt/ovmf/PcdSetNxForStack".\r
249 * @return EFI_SUCCESS Successfully fetch the settings.\r
250 */\r
251EFI_STATUS\r
252EFIAPI\r
253PlatformNoexecDxeInitialization (\r
254 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
255 )\r
256{\r
257 return QemuFwCfgParseBool ("opt/ovmf/PcdSetNxForStack", &PlatformInfoHob->PcdSetNxForStack);\r
258}\r
259\r
260VOID\r
261PciExBarInitialization (\r
262 VOID\r
263 )\r
264{\r
265 union {\r
266 UINT64 Uint64;\r
267 UINT32 Uint32[2];\r
268 } PciExBarBase;\r
269\r
270 //\r
271 // We only support the 256MB size for the MMCONFIG area:\r
272 // 256 buses * 32 devices * 8 functions * 4096 bytes config space.\r
273 //\r
274 // The masks used below enforce the Q35 requirements that the MMCONFIG area\r
275 // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB.\r
276 //\r
277 // Note that (b) also ensures that the minimum address width we have\r
278 // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice\r
279 // for DXE's page tables to cover the MMCONFIG area.\r
280 //\r
47f44097 281 PciExBarBase.Uint64 = PcdGet64 (PcdPciExpressBaseAddress);\r
96047b66
MX
282 ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0);\r
283 ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0);\r
284\r
285 //\r
286 // Clear the PCIEXBAREN bit first, before programming the high register.\r
287 //\r
288 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0);\r
289\r
290 //\r
291 // Program the high register. Then program the low register, setting the\r
292 // MMCONFIG area size and enabling decoding at once.\r
293 //\r
294 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]);\r
295 PciWrite32 (\r
296 DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW),\r
297 PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN\r
298 );\r
299}\r
300\r
301VOID\r
302EFIAPI\r
303PlatformMiscInitialization (\r
304 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
305 )\r
306{\r
307 UINTN PmCmd;\r
308 UINTN Pmba;\r
309 UINT32 PmbaAndVal;\r
310 UINT32 PmbaOrVal;\r
311 UINTN AcpiCtlReg;\r
312 UINT8 AcpiEnBit;\r
313\r
314 //\r
315 // Disable A20 Mask\r
316 //\r
317 IoOr8 (0x92, BIT1);\r
318\r
319 //\r
320 // Build the CPU HOB with guest RAM size dependent address width and 16-bits\r
321 // of IO space. (Side note: unlike other HOBs, the CPU HOB is needed during\r
322 // S3 resume as well, so we build it unconditionally.)\r
323 //\r
324 BuildCpuHob (PlatformInfoHob->PhysMemAddressWidth, 16);\r
325\r
326 //\r
327 // Determine platform type and save Host Bridge DID to PCD\r
328 //\r
329 switch (PlatformInfoHob->HostBridgeDevId) {\r
330 case INTEL_82441_DEVICE_ID:\r
331 PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET);\r
332 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
333 PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;\r
334 PmbaOrVal = PIIX4_PMBA_VALUE;\r
335 AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);\r
336 AcpiEnBit = PIIX4_PMREGMISC_PMIOSE;\r
337 break;\r
338 case INTEL_Q35_MCH_DEVICE_ID:\r
339 PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET);\r
340 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
341 PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;\r
342 PmbaOrVal = ICH9_PMBASE_VALUE;\r
343 AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);\r
344 AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN;\r
345 break;\r
346 case CLOUDHV_DEVICE_ID:\r
347 break;\r
348 default:\r
349 DEBUG ((\r
350 DEBUG_ERROR,\r
351 "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
352 __FUNCTION__,\r
353 PlatformInfoHob->HostBridgeDevId\r
354 ));\r
355 ASSERT (FALSE);\r
356 return;\r
357 }\r
358\r
359 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {\r
360 DEBUG ((DEBUG_INFO, "%a: Cloud Hypervisor is done.\n", __FUNCTION__));\r
361 return;\r
362 }\r
363\r
364 //\r
365 // If the appropriate IOspace enable bit is set, assume the ACPI PMBA has\r
366 // been configured and skip the setup here. This matches the logic in\r
367 // AcpiTimerLibConstructor ().\r
368 //\r
369 if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {\r
370 //\r
371 // The PEI phase should be exited with fully accessibe ACPI PM IO space:\r
372 // 1. set PMBA\r
373 //\r
374 PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);\r
375\r
376 //\r
377 // 2. set PCICMD/IOSE\r
378 //\r
379 PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE);\r
380\r
381 //\r
382 // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN)\r
383 //\r
384 PciOr8 (AcpiCtlReg, AcpiEnBit);\r
385 }\r
386\r
387 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
388 //\r
389 // Set Root Complex Register Block BAR\r
390 //\r
391 PciWrite32 (\r
392 POWER_MGMT_REGISTER_Q35 (ICH9_RCBA),\r
393 ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN\r
394 );\r
395\r
396 //\r
397 // Set PCI Express Register Range Base Address\r
398 //\r
399 PciExBarInitialization ();\r
400 }\r
401}\r
402\r
403/**\r
404 Fetch the boot CPU count and the possible CPU count from QEMU, and expose\r
405 them to UefiCpuPkg modules.\r
406**/\r
407VOID\r
408EFIAPI\r
409PlatformMaxCpuCountInitialization (\r
410 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
411 )\r
412{\r
413 UINT16 BootCpuCount;\r
414 UINT32 MaxCpuCount;\r
415\r
416 //\r
417 // Try to fetch the boot CPU count.\r
418 //\r
419 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
420 BootCpuCount = QemuFwCfgRead16 ();\r
421 if (BootCpuCount == 0) {\r
422 //\r
423 // QEMU doesn't report the boot CPU count. (BootCpuCount == 0) will let\r
424 // MpInitLib count APs up to (PcdCpuMaxLogicalProcessorNumber - 1), or\r
425 // until PcdCpuApInitTimeOutInMicroSeconds elapses (whichever is reached\r
426 // first).\r
427 //\r
428 DEBUG ((DEBUG_WARN, "%a: boot CPU count unavailable\n", __FUNCTION__));\r
429 MaxCpuCount = PlatformInfoHob->DefaultMaxCpuNumber;\r
430 } else {\r
431 //\r
432 // We will expose BootCpuCount to MpInitLib. MpInitLib will count APs up to\r
433 // (BootCpuCount - 1) precisely, regardless of timeout.\r
434 //\r
435 // Now try to fetch the possible CPU count.\r
436 //\r
437 UINTN CpuHpBase;\r
438 UINT32 CmdData2;\r
439\r
440 CpuHpBase = ((PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) ?\r
441 ICH9_CPU_HOTPLUG_BASE : PIIX4_CPU_HOTPLUG_BASE);\r
442\r
443 //\r
444 // If only legacy mode is available in the CPU hotplug register block, or\r
445 // the register block is completely missing, then the writes below are\r
446 // no-ops.\r
447 //\r
448 // 1. Switch the hotplug register block to modern mode.\r
449 //\r
450 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);\r
451 //\r
452 // 2. Select a valid CPU for deterministic reading of\r
453 // QEMU_CPUHP_R_CMD_DATA2.\r
454 //\r
455 // CPU#0 is always valid; it is the always present and non-removable\r
456 // BSP.\r
457 //\r
458 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);\r
459 //\r
460 // 3. Send a command after which QEMU_CPUHP_R_CMD_DATA2 is specified to\r
461 // read as zero, and which does not invalidate the selector. (The\r
462 // selector may change, but it must not become invalid.)\r
463 //\r
464 // Send QEMU_CPUHP_CMD_GET_PENDING, as it will prove useful later.\r
465 //\r
466 IoWrite8 (CpuHpBase + QEMU_CPUHP_W_CMD, QEMU_CPUHP_CMD_GET_PENDING);\r
467 //\r
468 // 4. Read QEMU_CPUHP_R_CMD_DATA2.\r
469 //\r
470 // If the register block is entirely missing, then this is an unassigned\r
471 // IO read, returning all-bits-one.\r
472 //\r
473 // If only legacy mode is available, then bit#0 stands for CPU#0 in the\r
474 // "CPU present bitmap". CPU#0 is always present.\r
475 //\r
476 // Otherwise, QEMU_CPUHP_R_CMD_DATA2 is either still reserved (returning\r
477 // all-bits-zero), or it is specified to read as zero after the above\r
478 // steps. Both cases confirm modern mode.\r
479 //\r
480 CmdData2 = IoRead32 (CpuHpBase + QEMU_CPUHP_R_CMD_DATA2);\r
481 DEBUG ((DEBUG_VERBOSE, "%a: CmdData2=0x%x\n", __FUNCTION__, CmdData2));\r
482 if (CmdData2 != 0) {\r
483 //\r
484 // QEMU doesn't support the modern CPU hotplug interface. Assume that the\r
485 // possible CPU count equals the boot CPU count (precluding hotplug).\r
486 //\r
487 DEBUG ((\r
488 DEBUG_WARN,\r
489 "%a: modern CPU hotplug interface unavailable\n",\r
490 __FUNCTION__\r
491 ));\r
492 MaxCpuCount = BootCpuCount;\r
493 } else {\r
494 //\r
495 // Grab the possible CPU count from the modern CPU hotplug interface.\r
496 //\r
497 UINT32 Present, Possible, Selected;\r
498\r
499 Present = 0;\r
500 Possible = 0;\r
501\r
502 //\r
503 // We've sent QEMU_CPUHP_CMD_GET_PENDING last; this ensures\r
504 // QEMU_CPUHP_RW_CMD_DATA can now be read usefully. However,\r
505 // QEMU_CPUHP_CMD_GET_PENDING may have selected a CPU with actual pending\r
506 // hotplug events; therefore, select CPU#0 forcibly.\r
507 //\r
508 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);\r
509\r
510 do {\r
511 UINT8 CpuStatus;\r
512\r
513 //\r
514 // Read the status of the currently selected CPU. This will help with a\r
515 // sanity check against "BootCpuCount".\r
516 //\r
517 CpuStatus = IoRead8 (CpuHpBase + QEMU_CPUHP_R_CPU_STAT);\r
518 if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) != 0) {\r
519 ++Present;\r
520 }\r
521\r
522 //\r
523 // Attempt to select the next CPU.\r
524 //\r
525 ++Possible;\r
526 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);\r
527 //\r
528 // If the selection is successful, then the following read will return\r
529 // the selector (which we know is positive at this point). Otherwise,\r
530 // the read will return 0.\r
531 //\r
532 Selected = IoRead32 (CpuHpBase + QEMU_CPUHP_RW_CMD_DATA);\r
533 ASSERT (Selected == Possible || Selected == 0);\r
534 } while (Selected > 0);\r
535\r
536 //\r
537 // Sanity check: fw_cfg and the modern CPU hotplug interface should\r
538 // return the same boot CPU count.\r
539 //\r
540 if (BootCpuCount != Present) {\r
541 DEBUG ((\r
542 DEBUG_WARN,\r
543 "%a: QEMU v2.7 reset bug: BootCpuCount=%d "\r
544 "Present=%u\n",\r
545 __FUNCTION__,\r
546 BootCpuCount,\r
547 Present\r
548 ));\r
549 //\r
550 // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus\r
551 // platform reset (including S3), was corrected in QEMU commit\r
552 // e3cadac073a9 ("pc: fix FW_CFG_NB_CPUS to account for -device added\r
553 // CPUs", 2016-11-16), part of release v2.8.0.\r
554 //\r
555 BootCpuCount = (UINT16)Present;\r
556 }\r
557\r
558 MaxCpuCount = Possible;\r
559 }\r
560 }\r
561\r
562 DEBUG ((\r
563 DEBUG_INFO,\r
564 "%a: BootCpuCount=%d MaxCpuCount=%u\n",\r
565 __FUNCTION__,\r
566 BootCpuCount,\r
567 MaxCpuCount\r
568 ));\r
569 ASSERT (BootCpuCount <= MaxCpuCount);\r
570\r
571 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber = MaxCpuCount;\r
572 PlatformInfoHob->PcdCpuBootLogicalProcessorNumber = BootCpuCount;\r
573}\r