]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmfPkg: LoadLinuxLib: Fix kernel entry for 64-bit OVMF
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / Qemu.c
CommitLineData
522203de 1/** @file\r
2 OVMF ACPI QEMU support\r
3\r
4 Copyright (c) 2008 - 2012, Intel Corporation. All rights reserved.<BR>\r
14430c55 5\r
6 Copyright (C) 2012, Red Hat, Inc.\r
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
24#include <IndustryStandard/Acpi.h>\r
522203de 25\r
26BOOLEAN\r
27QemuDetected (\r
28 VOID\r
29 )\r
30{\r
31 if (!QemuFwCfgIsAvailable ()) {\r
32 return FALSE;\r
33 }\r
34\r
35 return TRUE;\r
36}\r
37\r
38\r
498f7d8d 39STATIC\r
40UINTN\r
41CountBits16 (\r
42 UINT16 Mask\r
43 )\r
44{\r
45 //\r
46 // For all N >= 1, N bits are enough to represent the number of bits set\r
47 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),\r
48 // the maximum number of possibly set bits increases by one, while the\r
49 // representable maximum doubles.\r
50 //\r
51 Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);\r
52 Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);\r
53 Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);\r
54 Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);\r
55\r
56 return Mask;\r
57}\r
58\r
59\r
255b4184 60STATIC\r
61EFI_STATUS\r
62EFIAPI\r
63QemuInstallAcpiMadtTable (\r
64 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
65 IN VOID *AcpiTableBuffer,\r
66 IN UINTN AcpiTableBufferSize,\r
67 OUT UINTN *TableKey\r
68 )\r
69{\r
498f7d8d 70 UINTN CpuCount;\r
71 UINTN PciLinkIsoCount;\r
72 UINTN NewBufferSize;\r
73 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;\r
74 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;\r
75 EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;\r
76 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;\r
77 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;\r
78 VOID *Ptr;\r
79 UINTN Loop;\r
80 EFI_STATUS Status;\r
81\r
82 ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
255b4184 83\r
84 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
498f7d8d 85 CpuCount = QemuFwCfgRead16 ();\r
86 ASSERT (CpuCount >= 1);\r
255b4184 87\r
88 //\r
498f7d8d 89 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset\r
90 // corresponds to the union of all possible interrupt assignments for the LNKA,\r
91 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.\r
255b4184 92 //\r
498f7d8d 93 PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
255b4184 94\r
498f7d8d 95 NewBufferSize = 1 * sizeof (*Madt) +\r
96 CpuCount * sizeof (*LocalApic) +\r
97 1 * sizeof (*IoApic) +\r
98 (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
99 1 * sizeof (*LocalApicNmi);\r
255b4184 100\r
498f7d8d 101 Madt = AllocatePool (NewBufferSize);\r
102 if (Madt == NULL) {\r
103 return EFI_OUT_OF_RESOURCES;\r
104 }\r
105\r
ff8dd312 106 CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
107 Madt->Header.Length = (UINT32) NewBufferSize;\r
498f7d8d 108 Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
109 Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;\r
110 Ptr = Madt + 1;\r
111\r
112 LocalApic = Ptr;\r
113 for (Loop = 0; Loop < CpuCount; ++Loop) {\r
114 LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
115 LocalApic->Length = sizeof (*LocalApic);\r
ff8dd312 116 LocalApic->AcpiProcessorId = (UINT8) Loop;\r
117 LocalApic->ApicId = (UINT8) Loop;\r
498f7d8d 118 LocalApic->Flags = 1; // enabled\r
119 ++LocalApic;\r
120 }\r
121 Ptr = LocalApic;\r
122\r
123 IoApic = Ptr;\r
124 IoApic->Type = EFI_ACPI_1_0_IO_APIC;\r
125 IoApic->Length = sizeof (*IoApic);\r
ff8dd312 126 IoApic->IoApicId = (UINT8) CpuCount;\r
498f7d8d 127 IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;\r
128 IoApic->IoApicAddress = 0xFEC00000;\r
129 IoApic->SystemVectorBase = 0x00000000;\r
130 Ptr = IoApic + 1;\r
255b4184 131\r
132 //\r
498f7d8d 133 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure\r
255b4184 134 //\r
498f7d8d 135 Iso = Ptr;\r
136 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
137 Iso->Length = sizeof (*Iso);\r
138 Iso->Bus = 0x00; // ISA\r
139 Iso->Source = 0x00; // IRQ0\r
140 Iso->GlobalSystemInterruptVector = 0x00000002;\r
141 Iso->Flags = 0x0000; // Conforms to specs of the bus\r
142 ++Iso;\r
255b4184 143\r
498f7d8d 144 //\r
145 // Set Level-tiggered, Active High for all possible PCI link targets.\r
146 //\r
147 for (Loop = 0; Loop < 16; ++Loop) {\r
148 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {\r
149 continue;\r
150 }\r
151 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
152 Iso->Length = sizeof (*Iso);\r
153 Iso->Bus = 0x00; // ISA\r
ff8dd312 154 Iso->Source = (UINT8) Loop;\r
155 Iso->GlobalSystemInterruptVector = (UINT32) Loop;\r
498f7d8d 156 Iso->Flags = 0x000D; // Level-tiggered, Active High\r
157 ++Iso;\r
158 }\r
159 ASSERT (\r
ff8dd312 160 (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==\r
498f7d8d 161 1 + PciLinkIsoCount\r
162 );\r
163 Ptr = Iso;\r
164\r
165 LocalApicNmi = Ptr;\r
166 LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;\r
167 LocalApicNmi->Length = sizeof (*LocalApicNmi);\r
168 LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors\r
169 //\r
170 // polarity and trigger mode of the APIC I/O input signals conform to the\r
171 // specifications of the bus\r
172 //\r
173 LocalApicNmi->Flags = 0x0000;\r
174 //\r
175 // Local APIC interrupt input LINTn to which NMI is connected.\r
176 //\r
177 LocalApicNmi->LocalApicInti = 0x01;\r
178 Ptr = LocalApicNmi + 1;\r
255b4184 179\r
ff8dd312 180 ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
498f7d8d 181 Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
255b4184 182\r
498f7d8d 183 FreePool (Madt);\r
255b4184 184\r
185 return Status;\r
186}\r
187\r
188\r
253a2ea7 189#pragma pack(1)\r
190\r
191typedef struct {\r
192 UINT64 Base;\r
193 UINT64 End;\r
194 UINT64 Length;\r
195} PCI_WINDOW;\r
196\r
197typedef struct {\r
198 PCI_WINDOW PciWindow32;\r
199 PCI_WINDOW PciWindow64;\r
200} FIRMWARE_DATA;\r
201\r
14430c55 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 UINT8 DWordPrefix;\r
210 UINT8 Pm1aCntSlpTyp;\r
211 UINT8 Pm1bCntSlpTyp;\r
212 UINT8 Reserved[2];\r
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
249 if (ExclTop <= BASE_4GB) {\r
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
329 0x07, // PkgLength\r
330 0x01, // NumElements\r
331 0x0c, // DWordPrefix\r
332 0x00, // Pm1aCntSlpTyp\r
333 0x00, // Pm1bCntSlpTyp -- we don't support it\r
334 { 0x00, 0x00 } // Reserved\r
335 };\r
336 RETURN_STATUS Status;\r
337 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
338 UINTN FwCfgSize;\r
339 UINT8 SystemStates[6];\r
340\r
341 //\r
342 // configure defaults\r
343 //\r
344 *SuspendToRamSize = sizeof Template;\r
345 CopyMem (SuspendToRam, &Template, sizeof Template);\r
346 SuspendToRam->NameChar[2] = '3'; // S3\r
347 SuspendToRam->Pm1aCntSlpTyp = 1; // PIIX4: STR\r
348\r
349 *SuspendToDiskSize = sizeof Template;\r
350 CopyMem (SuspendToDisk, &Template, sizeof Template);\r
351 SuspendToDisk->NameChar[2] = '4'; // S4\r
352 SuspendToDisk->Pm1aCntSlpTyp = 2; // PIIX4: POSCL\r
353\r
354 //\r
355 // check for overrides\r
356 //\r
357 Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);\r
358 if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {\r
359 DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));\r
360 return;\r
361 }\r
362 QemuFwCfgSelectItem (FwCfgItem);\r
363 QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);\r
364\r
365 //\r
366 // Each byte corresponds to a system state. In each byte, the MSB tells us\r
367 // whether the given state is enabled. If so, the three LSBs specify the\r
368 // value to be written to the PM control register's SUS_TYP bits.\r
369 //\r
370 if (SystemStates[3] & BIT7) {\r
371 SuspendToRam->Pm1aCntSlpTyp = SystemStates[3] & (BIT2 | BIT1 | BIT0);\r
372 DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n", SuspendToRam->Pm1aCntSlpTyp));\r
373 } else {\r
374 *SuspendToRamSize = 0;\r
375 DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));\r
376 }\r
377\r
378 if (SystemStates[4] & BIT7) {\r
379 SuspendToDisk->Pm1aCntSlpTyp = SystemStates[4] & (BIT2 | BIT1 | BIT0);\r
380 DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n", SuspendToDisk->Pm1aCntSlpTyp));\r
381 } else {\r
382 *SuspendToDiskSize = 0;\r
383 DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));\r
384 }\r
385}\r
386\r
387\r
253a2ea7 388STATIC\r
389EFI_STATUS\r
390EFIAPI\r
391QemuInstallAcpiSsdtTable (\r
392 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
393 IN VOID *AcpiTableBuffer,\r
394 IN UINTN AcpiTableBufferSize,\r
395 OUT UINTN *TableKey\r
396 )\r
397{\r
398 EFI_STATUS Status;\r
399 FIRMWARE_DATA *FwData;\r
400\r
401 Status = EFI_OUT_OF_RESOURCES;\r
402\r
403 FwData = AllocateReservedPool (sizeof (*FwData));\r
404 if (FwData != NULL) {\r
14430c55 405 UINTN SuspendToRamSize;\r
406 SYSTEM_STATE_PACKAGE SuspendToRam;\r
407 UINTN SuspendToDiskSize;\r
408 SYSTEM_STATE_PACKAGE SuspendToDisk;\r
409 UINTN SsdtSize;\r
410 UINT8 *Ssdt;\r
411\r
412 GetSuspendStates (&SuspendToRamSize, &SuspendToRam,\r
413 &SuspendToDiskSize, &SuspendToDisk);\r
414 SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;\r
253a2ea7 415 Ssdt = AllocatePool (SsdtSize);\r
416\r
417 if (Ssdt != NULL) {\r
418 Status = PopulateFwData (FwData);\r
419\r
420 if (Status == EFI_SUCCESS) {\r
421 UINT8 *SsdtPtr;\r
422\r
423 SsdtPtr = Ssdt;\r
424\r
425 CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);\r
426 SsdtPtr += AcpiTableBufferSize;\r
427\r
428 //\r
429 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"\r
430 //\r
431 *(SsdtPtr++) = 0x5B; // ExtOpPrefix\r
432 *(SsdtPtr++) = 0x80; // OpRegionOp\r
433 *(SsdtPtr++) = 'F';\r
434 *(SsdtPtr++) = 'W';\r
435 *(SsdtPtr++) = 'D';\r
436 *(SsdtPtr++) = 'T';\r
437 *(SsdtPtr++) = 0x00; // SystemMemory\r
438 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
439\r
440 //\r
441 // no virtual addressing yet, take the four least significant bytes\r
442 //\r
443 CopyMem(SsdtPtr, &FwData, 4);\r
444 SsdtPtr += 4;\r
445\r
446 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
447\r
448 *(UINT32*) SsdtPtr = sizeof (*FwData);\r
449 SsdtPtr += 4;\r
450\r
14430c55 451 //\r
452 // add suspend system states\r
453 //\r
454 CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);\r
455 SsdtPtr += SuspendToRamSize;\r
456 CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);\r
457 SsdtPtr += SuspendToDiskSize;\r
458\r
26af9aca 459 ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);\r
460 ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;\r
253a2ea7 461 Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);\r
462 }\r
463\r
464 FreePool(Ssdt);\r
465 }\r
466\r
467 if (Status != EFI_SUCCESS) {\r
468 FreePool(FwData);\r
469 }\r
470 }\r
471\r
472 return Status;\r
473}\r
474\r
475\r
522203de 476EFI_STATUS\r
477EFIAPI\r
478QemuInstallAcpiTable (\r
479 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
480 IN VOID *AcpiTableBuffer,\r
481 IN UINTN AcpiTableBufferSize,\r
482 OUT UINTN *TableKey\r
483 )\r
484{\r
255b4184 485 EFI_ACPI_DESCRIPTION_HEADER *Hdr;\r
486 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;\r
487\r
488 Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;\r
489 switch (Hdr->Signature) {\r
490 case EFI_ACPI_1_0_APIC_SIGNATURE:\r
491 TableInstallFunction = QemuInstallAcpiMadtTable;\r
492 break;\r
253a2ea7 493 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:\r
494 TableInstallFunction = QemuInstallAcpiSsdtTable;\r
495 break;\r
255b4184 496 default:\r
497 TableInstallFunction = InstallAcpiTable;\r
498 }\r
499\r
500 return TableInstallFunction (\r
522203de 501 AcpiProtocol,\r
502 AcpiTableBuffer,\r
503 AcpiTableBufferSize,\r
504 TableKey\r
505 );\r
506}\r
507\r