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