]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
BaseTools: Library hashing fix and optimization for --hash feature
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / Qemu.c
CommitLineData
522203de 1/** @file\r
2 OVMF ACPI QEMU support\r
3\r
ce883845 4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>\r
14430c55 5\r
2d1fe950 6 Copyright (C) 2012-2014, Red Hat, Inc.\r
14430c55 7\r
b26f0cf9 8 SPDX-License-Identifier: BSD-2-Clause-Patent\r
522203de 9\r
253a2ea7 10**/\r
522203de 11\r
12#include "AcpiPlatform.h"\r
255b4184 13#include <Library/BaseMemoryLib.h>\r
14#include <Library/MemoryAllocationLib.h>\r
522203de 15#include <Library/QemuFwCfgLib.h>\r
57c0beb6 16#include <Library/DxeServicesTableLib.h>\r
498f7d8d 17#include <Library/PcdLib.h>\r
387536e4 18#include <Library/OrderedCollectionLib.h>\r
498f7d8d 19#include <IndustryStandard/Acpi.h>\r
522203de 20\r
21BOOLEAN\r
22QemuDetected (\r
23 VOID\r
24 )\r
25{\r
26 if (!QemuFwCfgIsAvailable ()) {\r
27 return FALSE;\r
28 }\r
29\r
30 return TRUE;\r
31}\r
32\r
33\r
498f7d8d 34STATIC\r
35UINTN\r
36CountBits16 (\r
37 UINT16 Mask\r
38 )\r
39{\r
40 //\r
41 // For all N >= 1, N bits are enough to represent the number of bits set\r
42 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),\r
43 // the maximum number of possibly set bits increases by one, while the\r
44 // representable maximum doubles.\r
45 //\r
46 Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);\r
47 Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);\r
48 Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);\r
49 Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);\r
50\r
51 return Mask;\r
52}\r
53\r
54\r
255b4184 55STATIC\r
56EFI_STATUS\r
57EFIAPI\r
58QemuInstallAcpiMadtTable (\r
59 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
60 IN VOID *AcpiTableBuffer,\r
61 IN UINTN AcpiTableBufferSize,\r
62 OUT UINTN *TableKey\r
63 )\r
64{\r
498f7d8d 65 UINTN CpuCount;\r
66 UINTN PciLinkIsoCount;\r
67 UINTN NewBufferSize;\r
68 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;\r
69 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;\r
70 EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;\r
71 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;\r
72 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;\r
73 VOID *Ptr;\r
74 UINTN Loop;\r
75 EFI_STATUS Status;\r
76\r
77 ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
255b4184 78\r
79 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
498f7d8d 80 CpuCount = QemuFwCfgRead16 ();\r
81 ASSERT (CpuCount >= 1);\r
255b4184 82\r
83 //\r
498f7d8d 84 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset\r
85 // corresponds to the union of all possible interrupt assignments for the LNKA,\r
86 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.\r
255b4184 87 //\r
498f7d8d 88 PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
255b4184 89\r
498f7d8d 90 NewBufferSize = 1 * sizeof (*Madt) +\r
91 CpuCount * sizeof (*LocalApic) +\r
92 1 * sizeof (*IoApic) +\r
93 (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
94 1 * sizeof (*LocalApicNmi);\r
255b4184 95\r
498f7d8d 96 Madt = AllocatePool (NewBufferSize);\r
97 if (Madt == NULL) {\r
98 return EFI_OUT_OF_RESOURCES;\r
99 }\r
100\r
ff8dd312 101 CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
102 Madt->Header.Length = (UINT32) NewBufferSize;\r
498f7d8d 103 Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
104 Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;\r
105 Ptr = Madt + 1;\r
106\r
107 LocalApic = Ptr;\r
108 for (Loop = 0; Loop < CpuCount; ++Loop) {\r
109 LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
110 LocalApic->Length = sizeof (*LocalApic);\r
ff8dd312 111 LocalApic->AcpiProcessorId = (UINT8) Loop;\r
112 LocalApic->ApicId = (UINT8) Loop;\r
498f7d8d 113 LocalApic->Flags = 1; // enabled\r
114 ++LocalApic;\r
115 }\r
116 Ptr = LocalApic;\r
117\r
118 IoApic = Ptr;\r
119 IoApic->Type = EFI_ACPI_1_0_IO_APIC;\r
120 IoApic->Length = sizeof (*IoApic);\r
ff8dd312 121 IoApic->IoApicId = (UINT8) CpuCount;\r
498f7d8d 122 IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;\r
123 IoApic->IoApicAddress = 0xFEC00000;\r
124 IoApic->SystemVectorBase = 0x00000000;\r
125 Ptr = IoApic + 1;\r
255b4184 126\r
127 //\r
498f7d8d 128 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure\r
255b4184 129 //\r
498f7d8d 130 Iso = Ptr;\r
131 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
132 Iso->Length = sizeof (*Iso);\r
133 Iso->Bus = 0x00; // ISA\r
134 Iso->Source = 0x00; // IRQ0\r
135 Iso->GlobalSystemInterruptVector = 0x00000002;\r
136 Iso->Flags = 0x0000; // Conforms to specs of the bus\r
137 ++Iso;\r
255b4184 138\r
498f7d8d 139 //\r
140 // Set Level-tiggered, Active High for all possible PCI link targets.\r
141 //\r
142 for (Loop = 0; Loop < 16; ++Loop) {\r
143 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {\r
144 continue;\r
145 }\r
146 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
147 Iso->Length = sizeof (*Iso);\r
148 Iso->Bus = 0x00; // ISA\r
ff8dd312 149 Iso->Source = (UINT8) Loop;\r
150 Iso->GlobalSystemInterruptVector = (UINT32) Loop;\r
498f7d8d 151 Iso->Flags = 0x000D; // Level-tiggered, Active High\r
152 ++Iso;\r
153 }\r
154 ASSERT (\r
ff8dd312 155 (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==\r
498f7d8d 156 1 + PciLinkIsoCount\r
157 );\r
158 Ptr = Iso;\r
159\r
160 LocalApicNmi = Ptr;\r
161 LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;\r
162 LocalApicNmi->Length = sizeof (*LocalApicNmi);\r
163 LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors\r
164 //\r
165 // polarity and trigger mode of the APIC I/O input signals conform to the\r
166 // specifications of the bus\r
167 //\r
168 LocalApicNmi->Flags = 0x0000;\r
169 //\r
170 // Local APIC interrupt input LINTn to which NMI is connected.\r
171 //\r
172 LocalApicNmi->LocalApicInti = 0x01;\r
173 Ptr = LocalApicNmi + 1;\r
255b4184 174\r
ff8dd312 175 ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
498f7d8d 176 Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
255b4184 177\r
498f7d8d 178 FreePool (Madt);\r
255b4184 179\r
180 return Status;\r
181}\r
182\r
183\r
253a2ea7 184#pragma pack(1)\r
185\r
186typedef struct {\r
187 UINT64 Base;\r
188 UINT64 End;\r
189 UINT64 Length;\r
190} PCI_WINDOW;\r
191\r
192typedef struct {\r
193 PCI_WINDOW PciWindow32;\r
194 PCI_WINDOW PciWindow64;\r
195} FIRMWARE_DATA;\r
196\r
14430c55 197typedef struct {\r
3d80db51 198 UINT8 BytePrefix;\r
199 UINT8 ByteValue;\r
200} AML_BYTE;\r
201\r
202typedef struct {\r
203 UINT8 NameOp;\r
204 UINT8 RootChar;\r
205 UINT8 NameChar[4];\r
206 UINT8 PackageOp;\r
207 UINT8 PkgLength;\r
208 UINT8 NumElements;\r
209 AML_BYTE Pm1aCntSlpTyp;\r
210 AML_BYTE Pm1bCntSlpTyp;\r
211 AML_BYTE Reserved[2];\r
14430c55 212} SYSTEM_STATE_PACKAGE;\r
213\r
253a2ea7 214#pragma pack()\r
215\r
216\r
217STATIC\r
218EFI_STATUS\r
219EFIAPI\r
220PopulateFwData(\r
221 OUT FIRMWARE_DATA *FwData\r
222 )\r
223{\r
57c0beb6 224 EFI_STATUS Status;\r
225 UINTN NumDesc;\r
226 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;\r
227\r
228 Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);\r
229 if (Status == EFI_SUCCESS) {\r
230 UINT64 NonMmio32MaxExclTop;\r
231 UINT64 Mmio32MinBase;\r
232 UINT64 Mmio32MaxExclTop;\r
233 UINTN CurDesc;\r
234\r
235 Status = EFI_UNSUPPORTED;\r
236\r
237 NonMmio32MaxExclTop = 0;\r
238 Mmio32MinBase = BASE_4GB;\r
239 Mmio32MaxExclTop = 0;\r
240\r
241 for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {\r
242 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
243 UINT64 ExclTop;\r
244\r
245 Desc = &AllDesc[CurDesc];\r
246 ExclTop = Desc->BaseAddress + Desc->Length;\r
247\r
2662fa8d 248 if (ExclTop <= (UINT64) PcdGet32 (PcdOvmfFdBaseAddress)) {\r
57c0beb6 249 switch (Desc->GcdMemoryType) {\r
250 case EfiGcdMemoryTypeNonExistent:\r
251 break;\r
252\r
253 case EfiGcdMemoryTypeReserved:\r
254 case EfiGcdMemoryTypeSystemMemory:\r
255 if (NonMmio32MaxExclTop < ExclTop) {\r
256 NonMmio32MaxExclTop = ExclTop;\r
257 }\r
258 break;\r
259\r
260 case EfiGcdMemoryTypeMemoryMappedIo:\r
261 if (Mmio32MinBase > Desc->BaseAddress) {\r
262 Mmio32MinBase = Desc->BaseAddress;\r
263 }\r
264 if (Mmio32MaxExclTop < ExclTop) {\r
265 Mmio32MaxExclTop = ExclTop;\r
266 }\r
267 break;\r
268\r
269 default:\r
270 ASSERT(0);\r
271 }\r
272 }\r
273 }\r
274\r
275 if (Mmio32MinBase < NonMmio32MaxExclTop) {\r
276 Mmio32MinBase = NonMmio32MaxExclTop;\r
277 }\r
278\r
279 if (Mmio32MinBase < Mmio32MaxExclTop) {\r
280 FwData->PciWindow32.Base = Mmio32MinBase;\r
281 FwData->PciWindow32.End = Mmio32MaxExclTop - 1;\r
282 FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;\r
283\r
284 FwData->PciWindow64.Base = 0;\r
285 FwData->PciWindow64.End = 0;\r
286 FwData->PciWindow64.Length = 0;\r
287\r
288 Status = EFI_SUCCESS;\r
289 }\r
290\r
291 FreePool (AllDesc);\r
292 }\r
293\r
294 DEBUG ((\r
295 DEBUG_INFO,\r
296 "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
297 FwData->PciWindow32.Base,\r
298 FwData->PciWindow32.End,\r
299 FwData->PciWindow32.Length\r
300 ));\r
301 DEBUG ((\r
302 DEBUG_INFO,\r
303 "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
304 FwData->PciWindow64.Base,\r
305 FwData->PciWindow64.End,\r
306 FwData->PciWindow64.Length\r
307 ));\r
308\r
309 return Status;\r
253a2ea7 310}\r
311\r
312\r
14430c55 313STATIC\r
314VOID\r
315EFIAPI\r
316GetSuspendStates (\r
317 UINTN *SuspendToRamSize,\r
318 SYSTEM_STATE_PACKAGE *SuspendToRam,\r
319 UINTN *SuspendToDiskSize,\r
320 SYSTEM_STATE_PACKAGE *SuspendToDisk\r
321 )\r
322{\r
323 STATIC CONST SYSTEM_STATE_PACKAGE Template = {\r
324 0x08, // NameOp\r
325 '\\', // RootChar\r
326 { '_', 'S', 'x', '_' }, // NameChar[4]\r
327 0x12, // PackageOp\r
3d80db51 328 0x0A, // PkgLength\r
329 0x04, // NumElements\r
330 { 0x0A, 0x00 }, // Pm1aCntSlpTyp\r
331 { 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it\r
332 { // Reserved[2]\r
333 { 0x0A, 0x00 },\r
334 { 0x0A, 0x00 }\r
335 }\r
14430c55 336 };\r
337 RETURN_STATUS Status;\r
338 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
339 UINTN FwCfgSize;\r
340 UINT8 SystemStates[6];\r
341\r
342 //\r
343 // configure defaults\r
344 //\r
345 *SuspendToRamSize = sizeof Template;\r
346 CopyMem (SuspendToRam, &Template, sizeof Template);\r
3d80db51 347 SuspendToRam->NameChar[2] = '3'; // S3\r
348 SuspendToRam->Pm1aCntSlpTyp.ByteValue = 1; // PIIX4: STR\r
14430c55 349\r
350 *SuspendToDiskSize = sizeof Template;\r
351 CopyMem (SuspendToDisk, &Template, sizeof Template);\r
3d80db51 352 SuspendToDisk->NameChar[2] = '4'; // S4\r
353 SuspendToDisk->Pm1aCntSlpTyp.ByteValue = 2; // PIIX4: POSCL\r
14430c55 354\r
355 //\r
356 // check for overrides\r
357 //\r
358 Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);\r
359 if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {\r
360 DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));\r
361 return;\r
362 }\r
363 QemuFwCfgSelectItem (FwCfgItem);\r
364 QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);\r
365\r
366 //\r
367 // Each byte corresponds to a system state. In each byte, the MSB tells us\r
368 // whether the given state is enabled. If so, the three LSBs specify the\r
369 // value to be written to the PM control register's SUS_TYP bits.\r
370 //\r
371 if (SystemStates[3] & BIT7) {\r
3d80db51 372 SuspendToRam->Pm1aCntSlpTyp.ByteValue =\r
373 SystemStates[3] & (BIT2 | BIT1 | BIT0);\r
374 DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n",\r
375 SuspendToRam->Pm1aCntSlpTyp.ByteValue));\r
14430c55 376 } else {\r
377 *SuspendToRamSize = 0;\r
378 DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));\r
379 }\r
380\r
381 if (SystemStates[4] & BIT7) {\r
3d80db51 382 SuspendToDisk->Pm1aCntSlpTyp.ByteValue =\r
383 SystemStates[4] & (BIT2 | BIT1 | BIT0);\r
384 DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n",\r
385 SuspendToDisk->Pm1aCntSlpTyp.ByteValue));\r
14430c55 386 } else {\r
387 *SuspendToDiskSize = 0;\r
388 DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));\r
389 }\r
390}\r
391\r
392\r
253a2ea7 393STATIC\r
394EFI_STATUS\r
395EFIAPI\r
396QemuInstallAcpiSsdtTable (\r
397 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
398 IN VOID *AcpiTableBuffer,\r
399 IN UINTN AcpiTableBufferSize,\r
400 OUT UINTN *TableKey\r
401 )\r
402{\r
403 EFI_STATUS Status;\r
404 FIRMWARE_DATA *FwData;\r
405\r
406 Status = EFI_OUT_OF_RESOURCES;\r
407\r
408 FwData = AllocateReservedPool (sizeof (*FwData));\r
409 if (FwData != NULL) {\r
14430c55 410 UINTN SuspendToRamSize;\r
411 SYSTEM_STATE_PACKAGE SuspendToRam;\r
412 UINTN SuspendToDiskSize;\r
413 SYSTEM_STATE_PACKAGE SuspendToDisk;\r
414 UINTN SsdtSize;\r
415 UINT8 *Ssdt;\r
416\r
417 GetSuspendStates (&SuspendToRamSize, &SuspendToRam,\r
418 &SuspendToDiskSize, &SuspendToDisk);\r
419 SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;\r
253a2ea7 420 Ssdt = AllocatePool (SsdtSize);\r
421\r
422 if (Ssdt != NULL) {\r
423 Status = PopulateFwData (FwData);\r
424\r
425 if (Status == EFI_SUCCESS) {\r
426 UINT8 *SsdtPtr;\r
427\r
428 SsdtPtr = Ssdt;\r
429\r
430 CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);\r
431 SsdtPtr += AcpiTableBufferSize;\r
432\r
433 //\r
434 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"\r
435 //\r
436 *(SsdtPtr++) = 0x5B; // ExtOpPrefix\r
437 *(SsdtPtr++) = 0x80; // OpRegionOp\r
438 *(SsdtPtr++) = 'F';\r
439 *(SsdtPtr++) = 'W';\r
440 *(SsdtPtr++) = 'D';\r
441 *(SsdtPtr++) = 'T';\r
442 *(SsdtPtr++) = 0x00; // SystemMemory\r
443 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
444\r
445 //\r
446 // no virtual addressing yet, take the four least significant bytes\r
447 //\r
448 CopyMem(SsdtPtr, &FwData, 4);\r
449 SsdtPtr += 4;\r
450\r
451 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
452\r
453 *(UINT32*) SsdtPtr = sizeof (*FwData);\r
454 SsdtPtr += 4;\r
455\r
14430c55 456 //\r
457 // add suspend system states\r
458 //\r
459 CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);\r
460 SsdtPtr += SuspendToRamSize;\r
461 CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);\r
462 SsdtPtr += SuspendToDiskSize;\r
463\r
26af9aca 464 ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);\r
465 ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;\r
253a2ea7 466 Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);\r
467 }\r
468\r
469 FreePool(Ssdt);\r
470 }\r
471\r
472 if (Status != EFI_SUCCESS) {\r
473 FreePool(FwData);\r
474 }\r
475 }\r
476\r
477 return Status;\r
478}\r
479\r
480\r
522203de 481EFI_STATUS\r
482EFIAPI\r
483QemuInstallAcpiTable (\r
484 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
485 IN VOID *AcpiTableBuffer,\r
486 IN UINTN AcpiTableBufferSize,\r
487 OUT UINTN *TableKey\r
488 )\r
489{\r
255b4184 490 EFI_ACPI_DESCRIPTION_HEADER *Hdr;\r
491 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;\r
492\r
493 Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;\r
494 switch (Hdr->Signature) {\r
495 case EFI_ACPI_1_0_APIC_SIGNATURE:\r
496 TableInstallFunction = QemuInstallAcpiMadtTable;\r
497 break;\r
253a2ea7 498 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:\r
499 TableInstallFunction = QemuInstallAcpiSsdtTable;\r
500 break;\r
255b4184 501 default:\r
502 TableInstallFunction = InstallAcpiTable;\r
503 }\r
504\r
505 return TableInstallFunction (\r
522203de 506 AcpiProtocol,\r
507 AcpiTableBuffer,\r
508 AcpiTableBufferSize,\r
509 TableKey\r
510 );\r
511}\r