]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/PlatformInitLib/Platform.c
OvmfPkg/PlatformInitLib: factor out PlatformCpuCountBugCheck()
[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
4f173db8 28#include <Library/MemoryAllocationLib.h>\r
96047b66
MX
29#include <Library/QemuFwCfgLib.h>\r
30#include <Library/QemuFwCfgS3Lib.h>\r
31#include <Library/QemuFwCfgSimpleParserLib.h>\r
32#include <Library/PciLib.h>\r
4f173db8
MX
33#include <Guid/SystemNvDataGuid.h>\r
34#include <Guid/VariableFormat.h>\r
96047b66
MX
35#include <OvmfPlatforms.h>\r
36\r
102cafed
MX
37#include <Library/PlatformInitLib.h>\r
38\r
39VOID\r
40EFIAPI\r
41PlatformAddIoMemoryBaseSizeHob (\r
42 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
43 IN UINT64 MemorySize\r
44 )\r
45{\r
46 BuildResourceDescriptorHob (\r
47 EFI_RESOURCE_MEMORY_MAPPED_IO,\r
48 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
49 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
50 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
51 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
52 MemoryBase,\r
53 MemorySize\r
54 );\r
55}\r
56\r
57VOID\r
58EFIAPI\r
59PlatformAddReservedMemoryBaseSizeHob (\r
60 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
61 IN UINT64 MemorySize,\r
62 IN BOOLEAN Cacheable\r
63 )\r
64{\r
65 BuildResourceDescriptorHob (\r
66 EFI_RESOURCE_MEMORY_RESERVED,\r
67 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
68 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
69 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
70 (Cacheable ?\r
71 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
72 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
73 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE :\r
74 0\r
75 ) |\r
76 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
77 MemoryBase,\r
78 MemorySize\r
79 );\r
80}\r
81\r
82VOID\r
83EFIAPI\r
84PlatformAddIoMemoryRangeHob (\r
85 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
86 IN EFI_PHYSICAL_ADDRESS MemoryLimit\r
87 )\r
88{\r
89 PlatformAddIoMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));\r
90}\r
91\r
92VOID\r
93EFIAPI\r
94PlatformAddMemoryBaseSizeHob (\r
95 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
96 IN UINT64 MemorySize\r
97 )\r
98{\r
99 BuildResourceDescriptorHob (\r
100 EFI_RESOURCE_SYSTEM_MEMORY,\r
101 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
102 EFI_RESOURCE_ATTRIBUTE_INITIALIZED |\r
103 EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE |\r
104 EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE |\r
105 EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE |\r
106 EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE |\r
107 EFI_RESOURCE_ATTRIBUTE_TESTED,\r
108 MemoryBase,\r
109 MemorySize\r
110 );\r
111}\r
112\r
113VOID\r
114EFIAPI\r
115PlatformAddMemoryRangeHob (\r
116 IN EFI_PHYSICAL_ADDRESS MemoryBase,\r
117 IN EFI_PHYSICAL_ADDRESS MemoryLimit\r
118 )\r
119{\r
120 PlatformAddMemoryBaseSizeHob (MemoryBase, (UINT64)(MemoryLimit - MemoryBase));\r
121}\r
96047b66
MX
122\r
123VOID\r
124EFIAPI\r
125PlatformMemMapInitialization (\r
126 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
127 )\r
128{\r
129 UINT64 PciIoBase;\r
130 UINT64 PciIoSize;\r
96047b66
MX
131 UINT64 PciExBarBase;\r
132 UINT32 PciBase;\r
133 UINT32 PciSize;\r
134\r
135 PciIoBase = 0xC000;\r
136 PciIoSize = 0x4000;\r
137\r
138 //\r
139 // Video memory + Legacy BIOS region\r
140 //\r
e23f8f52
MX
141 if (!TdIsEnabled ()) {\r
142 PlatformAddIoMemoryRangeHob (0x0A0000, BASE_1MB);\r
143 }\r
96047b66
MX
144\r
145 if (PlatformInfoHob->HostBridgeDevId == 0xffff /* microvm */) {\r
146 PlatformAddIoMemoryBaseSizeHob (MICROVM_GED_MMIO_BASE, SIZE_4KB);\r
147 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB); /* ioapic #1 */\r
148 PlatformAddIoMemoryBaseSizeHob (0xFEC10000, SIZE_4KB); /* ioapic #2 */\r
149 return;\r
150 }\r
151\r
124b7650 152 PlatformGetSystemMemorySizeBelow4gb (PlatformInfoHob);\r
96047b66
MX
153 PciExBarBase = 0;\r
154 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
155 //\r
156 // The MMCONFIG area is expected to fall between the top of low RAM and\r
157 // the base of the 32-bit PCI host aperture.\r
158 //\r
47f44097 159 PciExBarBase = PcdGet64 (PcdPciExpressBaseAddress);\r
124b7650 160 ASSERT (PlatformInfoHob->LowMemory <= PciExBarBase);\r
96047b66
MX
161 ASSERT (PciExBarBase <= MAX_UINT32 - SIZE_256MB);\r
162 PciBase = (UINT32)(PciExBarBase + SIZE_256MB);\r
163 } else {\r
124b7650 164 ASSERT (PlatformInfoHob->LowMemory <= PlatformInfoHob->Uc32Base);\r
96047b66
MX
165 PciBase = PlatformInfoHob->Uc32Base;\r
166 }\r
167\r
168 //\r
169 // address purpose size\r
170 // ------------ -------- -------------------------\r
171 // max(top, 2g) PCI MMIO 0xFC000000 - max(top, 2g)\r
172 // 0xFC000000 gap 44 MB\r
173 // 0xFEC00000 IO-APIC 4 KB\r
174 // 0xFEC01000 gap 1020 KB\r
175 // 0xFED00000 HPET 1 KB\r
176 // 0xFED00400 gap 111 KB\r
177 // 0xFED1C000 gap (PIIX4) / RCRB (ICH9) 16 KB\r
178 // 0xFED20000 gap 896 KB\r
179 // 0xFEE00000 LAPIC 1 MB\r
180 //\r
181 PciSize = 0xFC000000 - PciBase;\r
182 PlatformAddIoMemoryBaseSizeHob (PciBase, PciSize);\r
183\r
184 PlatformInfoHob->PcdPciMmio32Base = PciBase;\r
185 PlatformInfoHob->PcdPciMmio32Size = PciSize;\r
186\r
187 PlatformAddIoMemoryBaseSizeHob (0xFEC00000, SIZE_4KB);\r
188 PlatformAddIoMemoryBaseSizeHob (0xFED00000, SIZE_1KB);\r
189 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
190 PlatformAddIoMemoryBaseSizeHob (ICH9_ROOT_COMPLEX_BASE, SIZE_16KB);\r
191 //\r
192 // Note: there should be an\r
193 //\r
194 // PlatformAddIoMemoryBaseSizeHob (PciExBarBase, SIZE_256MB);\r
195 //\r
196 // call below, just like the one above for RCBA. However, Linux insists\r
197 // that the MMCONFIG area be marked in the E820 or UEFI memory map as\r
198 // "reserved memory" -- Linux does not content itself with a simple gap\r
199 // in the memory map wherever the MCFG ACPI table points to.\r
200 //\r
201 // This appears to be a safety measure. The PCI Firmware Specification\r
202 // (rev 3.1) says in 4.1.2. "MCFG Table Description": "The resources can\r
203 // *optionally* be returned in [...] EFIGetMemoryMap as reserved memory\r
204 // [...]". (Emphasis added here.)\r
205 //\r
206 // Normally we add memory resource descriptor HOBs in\r
207 // QemuInitializeRam(), and pre-allocate from those with memory\r
208 // allocation HOBs in InitializeRamRegions(). However, the MMCONFIG area\r
209 // is most definitely not RAM; so, as an exception, cover it with\r
210 // uncacheable reserved memory right here.\r
211 //\r
212 PlatformAddReservedMemoryBaseSizeHob (PciExBarBase, SIZE_256MB, FALSE);\r
213 BuildMemoryAllocationHob (\r
214 PciExBarBase,\r
215 SIZE_256MB,\r
216 EfiReservedMemoryType\r
217 );\r
218 }\r
219\r
220 PlatformAddIoMemoryBaseSizeHob (PcdGet32 (PcdCpuLocalApicBaseAddress), SIZE_1MB);\r
221\r
222 //\r
223 // On Q35, the IO Port space is available for PCI resource allocations from\r
224 // 0x6000 up.\r
225 //\r
226 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
227 PciIoBase = 0x6000;\r
228 PciIoSize = 0xA000;\r
229 ASSERT ((ICH9_PMBASE_VALUE & 0xF000) < PciIoBase);\r
230 }\r
231\r
232 //\r
233 // Add PCI IO Port space available for PCI resource allocations.\r
234 //\r
235 BuildResourceDescriptorHob (\r
236 EFI_RESOURCE_IO,\r
237 EFI_RESOURCE_ATTRIBUTE_PRESENT |\r
238 EFI_RESOURCE_ATTRIBUTE_INITIALIZED,\r
239 PciIoBase,\r
240 PciIoSize\r
241 );\r
242\r
243 PlatformInfoHob->PcdPciIoBase = PciIoBase;\r
244 PlatformInfoHob->PcdPciIoSize = PciIoSize;\r
245}\r
246\r
247/**\r
248 * Fetch "opt/ovmf/PcdSetNxForStack" from QEMU\r
249 *\r
250 * @param Setting The pointer to the setting of "/opt/ovmf/PcdSetNxForStack".\r
251 * @return EFI_SUCCESS Successfully fetch the settings.\r
252 */\r
253EFI_STATUS\r
254EFIAPI\r
255PlatformNoexecDxeInitialization (\r
256 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
257 )\r
258{\r
259 return QemuFwCfgParseBool ("opt/ovmf/PcdSetNxForStack", &PlatformInfoHob->PcdSetNxForStack);\r
260}\r
261\r
262VOID\r
263PciExBarInitialization (\r
264 VOID\r
265 )\r
266{\r
267 union {\r
268 UINT64 Uint64;\r
269 UINT32 Uint32[2];\r
270 } PciExBarBase;\r
271\r
272 //\r
273 // We only support the 256MB size for the MMCONFIG area:\r
274 // 256 buses * 32 devices * 8 functions * 4096 bytes config space.\r
275 //\r
276 // The masks used below enforce the Q35 requirements that the MMCONFIG area\r
277 // be (a) correctly aligned -- here at 256 MB --, (b) located under 64 GB.\r
278 //\r
279 // Note that (b) also ensures that the minimum address width we have\r
280 // determined in AddressWidthInitialization(), i.e., 36 bits, will suffice\r
281 // for DXE's page tables to cover the MMCONFIG area.\r
282 //\r
47f44097 283 PciExBarBase.Uint64 = PcdGet64 (PcdPciExpressBaseAddress);\r
96047b66
MX
284 ASSERT ((PciExBarBase.Uint32[1] & MCH_PCIEXBAR_HIGHMASK) == 0);\r
285 ASSERT ((PciExBarBase.Uint32[0] & MCH_PCIEXBAR_LOWMASK) == 0);\r
286\r
287 //\r
288 // Clear the PCIEXBAREN bit first, before programming the high register.\r
289 //\r
290 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW), 0);\r
291\r
292 //\r
293 // Program the high register. Then program the low register, setting the\r
294 // MMCONFIG area size and enabling decoding at once.\r
295 //\r
296 PciWrite32 (DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_HIGH), PciExBarBase.Uint32[1]);\r
297 PciWrite32 (\r
298 DRAMC_REGISTER_Q35 (MCH_PCIEXBAR_LOW),\r
299 PciExBarBase.Uint32[0] | MCH_PCIEXBAR_BUS_FF | MCH_PCIEXBAR_EN\r
300 );\r
301}\r
302\r
303VOID\r
304EFIAPI\r
305PlatformMiscInitialization (\r
306 IN EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
307 )\r
308{\r
309 UINTN PmCmd;\r
310 UINTN Pmba;\r
311 UINT32 PmbaAndVal;\r
312 UINT32 PmbaOrVal;\r
313 UINTN AcpiCtlReg;\r
314 UINT8 AcpiEnBit;\r
315\r
316 //\r
317 // Disable A20 Mask\r
318 //\r
bf25f27e
SB
319 if (PlatformInfoHob->HostBridgeDevId != CLOUDHV_DEVICE_ID) {\r
320 IoOr8 (0x92, BIT1);\r
321 }\r
96047b66
MX
322\r
323 //\r
324 // Build the CPU HOB with guest RAM size dependent address width and 16-bits\r
325 // of IO space. (Side note: unlike other HOBs, the CPU HOB is needed during\r
326 // S3 resume as well, so we build it unconditionally.)\r
327 //\r
328 BuildCpuHob (PlatformInfoHob->PhysMemAddressWidth, 16);\r
329\r
330 //\r
331 // Determine platform type and save Host Bridge DID to PCD\r
332 //\r
333 switch (PlatformInfoHob->HostBridgeDevId) {\r
334 case INTEL_82441_DEVICE_ID:\r
335 PmCmd = POWER_MGMT_REGISTER_PIIX4 (PCI_COMMAND_OFFSET);\r
336 Pmba = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);\r
337 PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;\r
338 PmbaOrVal = PIIX4_PMBA_VALUE;\r
339 AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);\r
340 AcpiEnBit = PIIX4_PMREGMISC_PMIOSE;\r
341 break;\r
342 case INTEL_Q35_MCH_DEVICE_ID:\r
343 PmCmd = POWER_MGMT_REGISTER_Q35 (PCI_COMMAND_OFFSET);\r
344 Pmba = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);\r
345 PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;\r
346 PmbaOrVal = ICH9_PMBASE_VALUE;\r
347 AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);\r
348 AcpiEnBit = ICH9_ACPI_CNTL_ACPI_EN;\r
349 break;\r
350 case CLOUDHV_DEVICE_ID:\r
351 break;\r
352 default:\r
353 DEBUG ((\r
354 DEBUG_ERROR,\r
355 "%a: Unknown Host Bridge Device ID: 0x%04x\n",\r
356 __FUNCTION__,\r
357 PlatformInfoHob->HostBridgeDevId\r
358 ));\r
359 ASSERT (FALSE);\r
360 return;\r
361 }\r
362\r
363 if (PlatformInfoHob->HostBridgeDevId == CLOUDHV_DEVICE_ID) {\r
364 DEBUG ((DEBUG_INFO, "%a: Cloud Hypervisor is done.\n", __FUNCTION__));\r
365 return;\r
366 }\r
367\r
368 //\r
369 // If the appropriate IOspace enable bit is set, assume the ACPI PMBA has\r
370 // been configured and skip the setup here. This matches the logic in\r
371 // AcpiTimerLibConstructor ().\r
372 //\r
373 if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {\r
374 //\r
375 // The PEI phase should be exited with fully accessibe ACPI PM IO space:\r
376 // 1. set PMBA\r
377 //\r
378 PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);\r
379\r
380 //\r
381 // 2. set PCICMD/IOSE\r
382 //\r
383 PciOr8 (PmCmd, EFI_PCI_COMMAND_IO_SPACE);\r
384\r
385 //\r
386 // 3. set ACPI PM IO enable bit (PMREGMISC:PMIOSE or ACPI_CNTL:ACPI_EN)\r
387 //\r
388 PciOr8 (AcpiCtlReg, AcpiEnBit);\r
389 }\r
390\r
391 if (PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) {\r
392 //\r
393 // Set Root Complex Register Block BAR\r
394 //\r
395 PciWrite32 (\r
396 POWER_MGMT_REGISTER_Q35 (ICH9_RCBA),\r
397 ICH9_ROOT_COMPLEX_BASE | ICH9_RCBA_EN\r
398 );\r
399\r
400 //\r
401 // Set PCI Express Register Range Base Address\r
402 //\r
403 PciExBarInitialization ();\r
404 }\r
405}\r
406\r
c3e128a4
LE
407/**\r
408 Check for various QEMU bugs concerning CPU numbers.\r
409\r
410 Compensate for those bugs if various conditions are satisfied, by updating a\r
411 suitable subset of the input-output parameters. The function may not return\r
412 (it may hang deliberately), even in RELEASE builds, if the QEMU bug is\r
413 impossible to cover up.\r
414\r
415 @param[in,out] BootCpuCount On input, the boot CPU count reported by QEMU via\r
416 fw_cfg (QemuFwCfgItemSmpCpuCount). The caller is\r
417 responsible for ensuring (BootCpuCount > 0); that\r
418 is, if QEMU does not provide the boot CPU count\r
419 via fw_cfg *at all*, then this function must not\r
420 be called.\r
421\r
422 @param[in,out] Present On input, the number of present-at-boot CPUs, as\r
423 reported by QEMU through the modern CPU hotplug\r
424 register block.\r
425\r
426 @param[in,out] Possible On input, the number of possible CPUs, as\r
427 reported by QEMU through the modern CPU hotplug\r
428 register block.\r
429**/\r
430STATIC\r
431VOID\r
432PlatformCpuCountBugCheck (\r
433 IN OUT UINT16 *BootCpuCount,\r
434 IN OUT UINT32 *Present,\r
435 IN OUT UINT32 *Possible\r
436 )\r
437{\r
438 ASSERT (*BootCpuCount > 0);\r
439\r
440 //\r
441 // Sanity check: fw_cfg and the modern CPU hotplug interface should expose the\r
442 // same boot CPU count.\r
443 //\r
444 if (*BootCpuCount != *Present) {\r
445 DEBUG ((\r
446 DEBUG_WARN,\r
447 "%a: QEMU v2.7 reset bug: BootCpuCount=%d Present=%u\n",\r
448 __FUNCTION__,\r
449 *BootCpuCount,\r
450 *Present\r
451 ));\r
452 //\r
453 // The handling of QemuFwCfgItemSmpCpuCount, across CPU hotplug plus\r
454 // platform reset (including S3), was corrected in QEMU commit e3cadac073a9\r
455 // ("pc: fix FW_CFG_NB_CPUS to account for -device added CPUs", 2016-11-16),\r
456 // part of release v2.8.0.\r
457 //\r
458 *BootCpuCount = (UINT16)*Present;\r
459 }\r
460}\r
461\r
96047b66
MX
462/**\r
463 Fetch the boot CPU count and the possible CPU count from QEMU, and expose\r
464 them to UefiCpuPkg modules.\r
465**/\r
466VOID\r
467EFIAPI\r
468PlatformMaxCpuCountInitialization (\r
469 IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob\r
470 )\r
471{\r
43f3cfce 472 UINT16 BootCpuCount = 0;\r
96047b66
MX
473 UINT32 MaxCpuCount;\r
474\r
475 //\r
476 // Try to fetch the boot CPU count.\r
477 //\r
43f3cfce
SB
478 if (QemuFwCfgIsAvailable ()) {\r
479 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
480 BootCpuCount = QemuFwCfgRead16 ();\r
481 }\r
482\r
96047b66
MX
483 if (BootCpuCount == 0) {\r
484 //\r
485 // QEMU doesn't report the boot CPU count. (BootCpuCount == 0) will let\r
486 // MpInitLib count APs up to (PcdCpuMaxLogicalProcessorNumber - 1), or\r
487 // until PcdCpuApInitTimeOutInMicroSeconds elapses (whichever is reached\r
488 // first).\r
489 //\r
490 DEBUG ((DEBUG_WARN, "%a: boot CPU count unavailable\n", __FUNCTION__));\r
491 MaxCpuCount = PlatformInfoHob->DefaultMaxCpuNumber;\r
492 } else {\r
493 //\r
494 // We will expose BootCpuCount to MpInitLib. MpInitLib will count APs up to\r
495 // (BootCpuCount - 1) precisely, regardless of timeout.\r
496 //\r
497 // Now try to fetch the possible CPU count.\r
498 //\r
499 UINTN CpuHpBase;\r
500 UINT32 CmdData2;\r
501\r
502 CpuHpBase = ((PlatformInfoHob->HostBridgeDevId == INTEL_Q35_MCH_DEVICE_ID) ?\r
503 ICH9_CPU_HOTPLUG_BASE : PIIX4_CPU_HOTPLUG_BASE);\r
504\r
505 //\r
506 // If only legacy mode is available in the CPU hotplug register block, or\r
507 // the register block is completely missing, then the writes below are\r
508 // no-ops.\r
509 //\r
510 // 1. Switch the hotplug register block to modern mode.\r
511 //\r
512 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);\r
513 //\r
514 // 2. Select a valid CPU for deterministic reading of\r
515 // QEMU_CPUHP_R_CMD_DATA2.\r
516 //\r
517 // CPU#0 is always valid; it is the always present and non-removable\r
518 // BSP.\r
519 //\r
520 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, 0);\r
521 //\r
522 // 3. Send a command after which QEMU_CPUHP_R_CMD_DATA2 is specified to\r
523 // read as zero, and which does not invalidate the selector. (The\r
524 // selector may change, but it must not become invalid.)\r
525 //\r
526 // Send QEMU_CPUHP_CMD_GET_PENDING, as it will prove useful later.\r
527 //\r
528 IoWrite8 (CpuHpBase + QEMU_CPUHP_W_CMD, QEMU_CPUHP_CMD_GET_PENDING);\r
529 //\r
530 // 4. Read QEMU_CPUHP_R_CMD_DATA2.\r
531 //\r
532 // If the register block is entirely missing, then this is an unassigned\r
533 // IO read, returning all-bits-one.\r
534 //\r
535 // If only legacy mode is available, then bit#0 stands for CPU#0 in the\r
536 // "CPU present bitmap". CPU#0 is always present.\r
537 //\r
538 // Otherwise, QEMU_CPUHP_R_CMD_DATA2 is either still reserved (returning\r
539 // all-bits-zero), or it is specified to read as zero after the above\r
540 // steps. Both cases confirm modern mode.\r
541 //\r
542 CmdData2 = IoRead32 (CpuHpBase + QEMU_CPUHP_R_CMD_DATA2);\r
543 DEBUG ((DEBUG_VERBOSE, "%a: CmdData2=0x%x\n", __FUNCTION__, CmdData2));\r
544 if (CmdData2 != 0) {\r
545 //\r
546 // QEMU doesn't support the modern CPU hotplug interface. Assume that the\r
547 // possible CPU count equals the boot CPU count (precluding hotplug).\r
548 //\r
549 DEBUG ((\r
550 DEBUG_WARN,\r
551 "%a: modern CPU hotplug interface unavailable\n",\r
552 __FUNCTION__\r
553 ));\r
554 MaxCpuCount = BootCpuCount;\r
555 } else {\r
556 //\r
557 // Grab the possible CPU count from the modern CPU hotplug interface.\r
558 //\r
559 UINT32 Present, Possible, Selected;\r
560\r
561 Present = 0;\r
562 Possible = 0;\r
563\r
564 //\r
565 // We've sent QEMU_CPUHP_CMD_GET_PENDING last; this ensures\r
566 // QEMU_CPUHP_RW_CMD_DATA can now be read usefully. However,\r
567 // QEMU_CPUHP_CMD_GET_PENDING may have selected a CPU with actual pending\r
568 // hotplug events; therefore, select CPU#0 forcibly.\r
569 //\r
570 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);\r
571\r
572 do {\r
573 UINT8 CpuStatus;\r
574\r
575 //\r
c3e128a4
LE
576 // Read the status of the currently selected CPU. This will help with\r
577 // various CPU count sanity checks.\r
96047b66
MX
578 //\r
579 CpuStatus = IoRead8 (CpuHpBase + QEMU_CPUHP_R_CPU_STAT);\r
580 if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) != 0) {\r
581 ++Present;\r
582 }\r
583\r
584 //\r
585 // Attempt to select the next CPU.\r
586 //\r
587 ++Possible;\r
588 IoWrite32 (CpuHpBase + QEMU_CPUHP_W_CPU_SEL, Possible);\r
589 //\r
590 // If the selection is successful, then the following read will return\r
591 // the selector (which we know is positive at this point). Otherwise,\r
592 // the read will return 0.\r
593 //\r
594 Selected = IoRead32 (CpuHpBase + QEMU_CPUHP_RW_CMD_DATA);\r
595 ASSERT (Selected == Possible || Selected == 0);\r
596 } while (Selected > 0);\r
597\r
c3e128a4 598 PlatformCpuCountBugCheck (&BootCpuCount, &Present, &Possible);\r
96047b66
MX
599\r
600 MaxCpuCount = Possible;\r
601 }\r
602 }\r
603\r
604 DEBUG ((\r
605 DEBUG_INFO,\r
606 "%a: BootCpuCount=%d MaxCpuCount=%u\n",\r
607 __FUNCTION__,\r
608 BootCpuCount,\r
609 MaxCpuCount\r
610 ));\r
611 ASSERT (BootCpuCount <= MaxCpuCount);\r
612\r
613 PlatformInfoHob->PcdCpuMaxLogicalProcessorNumber = MaxCpuCount;\r
614 PlatformInfoHob->PcdCpuBootLogicalProcessorNumber = BootCpuCount;\r
615}\r
4f173db8
MX
616\r
617/**\r
618 Check padding data all bit should be 1.\r
619\r
620 @param[in] Buffer - A pointer to buffer header\r
621 @param[in] BufferSize - Buffer size\r
622\r
623 @retval TRUE - The padding data is valid.\r
624 @retval TRUE - The padding data is invalid.\r
625\r
626**/\r
627BOOLEAN\r
628CheckPaddingData (\r
629 IN UINT8 *Buffer,\r
630 IN UINT32 BufferSize\r
631 )\r
632{\r
633 UINT32 index;\r
634\r
635 for (index = 0; index < BufferSize; index++) {\r
636 if (Buffer[index] != 0xFF) {\r
637 return FALSE;\r
638 }\r
639 }\r
640\r
641 return TRUE;\r
642}\r
643\r
644/**\r
645 Check the integrity of NvVarStore.\r
646\r
647 @param[in] NvVarStoreBase - A pointer to NvVarStore header\r
648 @param[in] NvVarStoreSize - NvVarStore size\r
649\r
650 @retval TRUE - The NvVarStore is valid.\r
651 @retval FALSE - The NvVarStore is invalid.\r
652\r
653**/\r
654BOOLEAN\r
655EFIAPI\r
656PlatformValidateNvVarStore (\r
657 IN UINT8 *NvVarStoreBase,\r
658 IN UINT32 NvVarStoreSize\r
659 )\r
660{\r
661 UINT16 Checksum;\r
662 UINTN VariableBase;\r
663 UINT32 VariableOffset;\r
664 UINT32 VariableOffsetBeforeAlign;\r
665 EFI_FIRMWARE_VOLUME_HEADER *NvVarStoreFvHeader;\r
666 VARIABLE_STORE_HEADER *NvVarStoreHeader;\r
667 AUTHENTICATED_VARIABLE_HEADER *VariableHeader;\r
668\r
669 static EFI_GUID FvHdrGUID = EFI_SYSTEM_NV_DATA_FV_GUID;\r
670 static EFI_GUID VarStoreHdrGUID = EFI_AUTHENTICATED_VARIABLE_GUID;\r
671\r
672 VariableOffset = 0;\r
673\r
674 if (NvVarStoreBase == NULL) {\r
675 DEBUG ((DEBUG_ERROR, "NvVarStore pointer is NULL.\n"));\r
676 return FALSE;\r
677 }\r
678\r
679 //\r
680 // Verify the header zerovetor, filesystemguid,\r
681 // revision, signature, attributes, fvlength, checksum\r
682 // HeaderLength cannot be an odd number\r
683 //\r
684 NvVarStoreFvHeader = (EFI_FIRMWARE_VOLUME_HEADER *)NvVarStoreBase;\r
685\r
686 if ((!IsZeroBuffer (NvVarStoreFvHeader->ZeroVector, 16)) ||\r
687 (!CompareGuid (&FvHdrGUID, &NvVarStoreFvHeader->FileSystemGuid)) ||\r
688 (NvVarStoreFvHeader->Signature != EFI_FVH_SIGNATURE) ||\r
689 (NvVarStoreFvHeader->Attributes != 0x4feff) ||\r
47d98838 690 ((NvVarStoreFvHeader->HeaderLength & 0x01) != 0) ||\r
4f173db8
MX
691 (NvVarStoreFvHeader->Revision != EFI_FVH_REVISION) ||\r
692 (NvVarStoreFvHeader->FvLength != NvVarStoreSize)\r
693 )\r
694 {\r
695 DEBUG ((DEBUG_ERROR, "NvVarStore FV headers were invalid.\n"));\r
696 return FALSE;\r
697 }\r
698\r
699 //\r
700 // Verify the header checksum\r
701 //\r
702 Checksum = CalculateSum16 ((VOID *)NvVarStoreFvHeader, NvVarStoreFvHeader->HeaderLength);\r
703\r
704 if (Checksum != 0) {\r
705 DEBUG ((DEBUG_ERROR, "NvVarStore FV checksum was invalid.\n"));\r
706 return FALSE;\r
707 }\r
708\r
709 //\r
710 // Verify the header signature, size, format, state\r
711 //\r
712 NvVarStoreHeader = (VARIABLE_STORE_HEADER *)(NvVarStoreBase + NvVarStoreFvHeader->HeaderLength);\r
713 if ((!CompareGuid (&VarStoreHdrGUID, &NvVarStoreHeader->Signature)) ||\r
714 (NvVarStoreHeader->Format != VARIABLE_STORE_FORMATTED) ||\r
715 (NvVarStoreHeader->State != VARIABLE_STORE_HEALTHY) ||\r
716 (NvVarStoreHeader->Size > (NvVarStoreFvHeader->FvLength - NvVarStoreFvHeader->HeaderLength)) ||\r
717 (NvVarStoreHeader->Size < sizeof (VARIABLE_STORE_HEADER))\r
718 )\r
719 {\r
720 DEBUG ((DEBUG_ERROR, "NvVarStore header signature/size/format/state were invalid.\n"));\r
721 return FALSE;\r
722 }\r
723\r
724 //\r
725 // Verify the header startId, state\r
726 // Verify data to the end\r
727 //\r
728 VariableBase = (UINTN)NvVarStoreBase + NvVarStoreFvHeader->HeaderLength + sizeof (VARIABLE_STORE_HEADER);\r
729 while (VariableOffset < (NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) {\r
730 VariableHeader = (AUTHENTICATED_VARIABLE_HEADER *)(VariableBase + VariableOffset);\r
731 if (VariableHeader->StartId != VARIABLE_DATA) {\r
732 if (!CheckPaddingData ((UINT8 *)VariableHeader, NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER) - VariableOffset)) {\r
733 DEBUG ((DEBUG_ERROR, "NvVarStore variable header StartId was invalid.\n"));\r
734 return FALSE;\r
735 }\r
736\r
737 VariableOffset = NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER);\r
738 } else {\r
ceb52713
CYL
739 if (!((VariableHeader->State == VAR_HEADER_VALID_ONLY) ||\r
740 (VariableHeader->State == VAR_ADDED) ||\r
741 (VariableHeader->State == (VAR_ADDED & VAR_DELETED)) ||\r
742 (VariableHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION)) ||\r
743 (VariableHeader->State == (VAR_ADDED & VAR_IN_DELETED_TRANSITION & VAR_DELETED))))\r
4f173db8
MX
744 {\r
745 DEBUG ((DEBUG_ERROR, "NvVarStore Variable header State was invalid.\n"));\r
746 return FALSE;\r
747 }\r
748\r
749 VariableOffset += sizeof (AUTHENTICATED_VARIABLE_HEADER) + VariableHeader->NameSize + VariableHeader->DataSize;\r
750 // Verify VariableOffset should be less than or equal NvVarStoreHeader->Size - sizeof(VARIABLE_STORE_HEADER)\r
751 if (VariableOffset > (NvVarStoreHeader->Size - sizeof (VARIABLE_STORE_HEADER))) {\r
752 DEBUG ((DEBUG_ERROR, "NvVarStore Variable header VariableOffset was invalid.\n"));\r
753 return FALSE;\r
754 }\r
755\r
756 VariableOffsetBeforeAlign = VariableOffset;\r
757 // 4 byte align\r
758 VariableOffset = (VariableOffset + 3) & (UINTN)(~3);\r
759\r
760 if (!CheckPaddingData ((UINT8 *)(VariableBase + VariableOffsetBeforeAlign), VariableOffset - VariableOffsetBeforeAlign)) {\r
761 DEBUG ((DEBUG_ERROR, "NvVarStore Variable header PaddingData was invalid.\n"));\r
762 return FALSE;\r
763 }\r
764 }\r
765 }\r
766\r
767 return TRUE;\r
768}\r
769\r
770/**\r
771 Allocate storage for NV variables early on so it will be\r
772 at a consistent address. Since VM memory is preserved\r
773 across reboots, this allows the NV variable storage to survive\r
774 a VM reboot.\r
775\r
776 *\r
777 * @retval VOID* The pointer to the storage for NV Variables\r
778 */\r
779VOID *\r
780EFIAPI\r
781PlatformReserveEmuVariableNvStore (\r
782 VOID\r
783 )\r
784{\r
785 VOID *VariableStore;\r
786 UINT32 VarStoreSize;\r
787\r
788 VarStoreSize = 2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
789 //\r
790 // Allocate storage for NV variables early on so it will be\r
791 // at a consistent address. Since VM memory is preserved\r
792 // across reboots, this allows the NV variable storage to survive\r
793 // a VM reboot.\r
794 //\r
795 VariableStore =\r
796 AllocateRuntimePages (\r
797 EFI_SIZE_TO_PAGES (VarStoreSize)\r
798 );\r
799 DEBUG ((\r
800 DEBUG_INFO,\r
801 "Reserved variable store memory: 0x%p; size: %dkb\n",\r
802 VariableStore,\r
803 VarStoreSize / 1024\r
804 ));\r
805\r
806 return VariableStore;\r
807}\r
808\r
809/**\r
810 When OVMF is lauched with -bios parameter, UEFI variables will be\r
811 partially emulated, and non-volatile variables may lose their contents\r
812 after a reboot. This makes the secure boot feature not working.\r
813\r
814 This function is used to initialize the EmuVariableNvStore\r
815 with the conent in PcdOvmfFlashNvStorageVariableBase.\r
816\r
817 @param[in] EmuVariableNvStore - A pointer to EmuVariableNvStore\r
818\r
819 @retval EFI_SUCCESS - Successfully init the EmuVariableNvStore\r
820 @retval Others - As the error code indicates\r
821 */\r
822EFI_STATUS\r
823EFIAPI\r
824PlatformInitEmuVariableNvStore (\r
825 IN VOID *EmuVariableNvStore\r
826 )\r
827{\r
828 UINT8 *Base;\r
829 UINT32 Size;\r
830 UINT32 EmuVariableNvStoreSize;\r
831\r
832 EmuVariableNvStoreSize = 2 * PcdGet32 (PcdFlashNvStorageFtwSpareSize);\r
833 if ((EmuVariableNvStore == NULL) || (EmuVariableNvStoreSize == 0)) {\r
834 DEBUG ((DEBUG_ERROR, "Invalid EmuVariableNvStore parameter.\n"));\r
835 return EFI_INVALID_PARAMETER;\r
836 }\r
837\r
838 Base = (UINT8 *)(UINTN)PcdGet32 (PcdOvmfFlashNvStorageVariableBase);\r
839 Size = (UINT32)PcdGet32 (PcdFlashNvStorageVariableSize);\r
840 ASSERT (Size < EmuVariableNvStoreSize);\r
841\r
842 if (!PlatformValidateNvVarStore (Base, PcdGet32 (PcdCfvRawDataSize))) {\r
843 ASSERT (FALSE);\r
844 return EFI_INVALID_PARAMETER;\r
845 }\r
846\r
847 DEBUG ((DEBUG_INFO, "Init EmuVariableNvStore with the content in FlashNvStorage\n"));\r
848\r
849 CopyMem (EmuVariableNvStore, Base, Size);\r
850\r
851 return EFI_SUCCESS;\r
852}\r