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