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