]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmfPkg: librarize reusable bits from VirtioBlkDxe's SynchronousRequest()
[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
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
253a2ea7 13**/\r
522203de 14\r
15#include "AcpiPlatform.h"\r
255b4184 16#include <Library/BaseMemoryLib.h>\r
17#include <Library/MemoryAllocationLib.h>\r
522203de 18#include <Library/QemuFwCfgLib.h>\r
57c0beb6 19#include <Library/DxeServicesTableLib.h>\r
498f7d8d 20#include <Library/PcdLib.h>\r
21#include <IndustryStandard/Acpi.h>\r
522203de 22\r
23BOOLEAN\r
24QemuDetected (\r
25 VOID\r
26 )\r
27{\r
28 if (!QemuFwCfgIsAvailable ()) {\r
29 return FALSE;\r
30 }\r
31\r
32 return TRUE;\r
33}\r
34\r
35\r
498f7d8d 36STATIC\r
37UINTN\r
38CountBits16 (\r
39 UINT16 Mask\r
40 )\r
41{\r
42 //\r
43 // For all N >= 1, N bits are enough to represent the number of bits set\r
44 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),\r
45 // the maximum number of possibly set bits increases by one, while the\r
46 // representable maximum doubles.\r
47 //\r
48 Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);\r
49 Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);\r
50 Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);\r
51 Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);\r
52\r
53 return Mask;\r
54}\r
55\r
56\r
255b4184 57STATIC\r
58EFI_STATUS\r
59EFIAPI\r
60QemuInstallAcpiMadtTable (\r
61 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
62 IN VOID *AcpiTableBuffer,\r
63 IN UINTN AcpiTableBufferSize,\r
64 OUT UINTN *TableKey\r
65 )\r
66{\r
498f7d8d 67 UINTN CpuCount;\r
68 UINTN PciLinkIsoCount;\r
69 UINTN NewBufferSize;\r
70 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;\r
71 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;\r
72 EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;\r
73 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;\r
74 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;\r
75 VOID *Ptr;\r
76 UINTN Loop;\r
77 EFI_STATUS Status;\r
78\r
79 ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
255b4184 80\r
81 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
498f7d8d 82 CpuCount = QemuFwCfgRead16 ();\r
83 ASSERT (CpuCount >= 1);\r
255b4184 84\r
85 //\r
498f7d8d 86 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset\r
87 // corresponds to the union of all possible interrupt assignments for the LNKA,\r
88 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.\r
255b4184 89 //\r
498f7d8d 90 PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
255b4184 91\r
498f7d8d 92 NewBufferSize = 1 * sizeof (*Madt) +\r
93 CpuCount * sizeof (*LocalApic) +\r
94 1 * sizeof (*IoApic) +\r
95 (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
96 1 * sizeof (*LocalApicNmi);\r
255b4184 97\r
498f7d8d 98 Madt = AllocatePool (NewBufferSize);\r
99 if (Madt == NULL) {\r
100 return EFI_OUT_OF_RESOURCES;\r
101 }\r
102\r
ff8dd312 103 CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
104 Madt->Header.Length = (UINT32) NewBufferSize;\r
498f7d8d 105 Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
106 Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;\r
107 Ptr = Madt + 1;\r
108\r
109 LocalApic = Ptr;\r
110 for (Loop = 0; Loop < CpuCount; ++Loop) {\r
111 LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
112 LocalApic->Length = sizeof (*LocalApic);\r
ff8dd312 113 LocalApic->AcpiProcessorId = (UINT8) Loop;\r
114 LocalApic->ApicId = (UINT8) Loop;\r
498f7d8d 115 LocalApic->Flags = 1; // enabled\r
116 ++LocalApic;\r
117 }\r
118 Ptr = LocalApic;\r
119\r
120 IoApic = Ptr;\r
121 IoApic->Type = EFI_ACPI_1_0_IO_APIC;\r
122 IoApic->Length = sizeof (*IoApic);\r
ff8dd312 123 IoApic->IoApicId = (UINT8) CpuCount;\r
498f7d8d 124 IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;\r
125 IoApic->IoApicAddress = 0xFEC00000;\r
126 IoApic->SystemVectorBase = 0x00000000;\r
127 Ptr = IoApic + 1;\r
255b4184 128\r
129 //\r
498f7d8d 130 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure\r
255b4184 131 //\r
498f7d8d 132 Iso = Ptr;\r
133 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
134 Iso->Length = sizeof (*Iso);\r
135 Iso->Bus = 0x00; // ISA\r
136 Iso->Source = 0x00; // IRQ0\r
137 Iso->GlobalSystemInterruptVector = 0x00000002;\r
138 Iso->Flags = 0x0000; // Conforms to specs of the bus\r
139 ++Iso;\r
255b4184 140\r
498f7d8d 141 //\r
142 // Set Level-tiggered, Active High for all possible PCI link targets.\r
143 //\r
144 for (Loop = 0; Loop < 16; ++Loop) {\r
145 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {\r
146 continue;\r
147 }\r
148 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
149 Iso->Length = sizeof (*Iso);\r
150 Iso->Bus = 0x00; // ISA\r
ff8dd312 151 Iso->Source = (UINT8) Loop;\r
152 Iso->GlobalSystemInterruptVector = (UINT32) Loop;\r
498f7d8d 153 Iso->Flags = 0x000D; // Level-tiggered, Active High\r
154 ++Iso;\r
155 }\r
156 ASSERT (\r
ff8dd312 157 (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==\r
498f7d8d 158 1 + PciLinkIsoCount\r
159 );\r
160 Ptr = Iso;\r
161\r
162 LocalApicNmi = Ptr;\r
163 LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;\r
164 LocalApicNmi->Length = sizeof (*LocalApicNmi);\r
165 LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors\r
166 //\r
167 // polarity and trigger mode of the APIC I/O input signals conform to the\r
168 // specifications of the bus\r
169 //\r
170 LocalApicNmi->Flags = 0x0000;\r
171 //\r
172 // Local APIC interrupt input LINTn to which NMI is connected.\r
173 //\r
174 LocalApicNmi->LocalApicInti = 0x01;\r
175 Ptr = LocalApicNmi + 1;\r
255b4184 176\r
ff8dd312 177 ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
498f7d8d 178 Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
255b4184 179\r
498f7d8d 180 FreePool (Madt);\r
255b4184 181\r
182 return Status;\r
183}\r
184\r
185\r
253a2ea7 186#pragma pack(1)\r
187\r
188typedef struct {\r
189 UINT64 Base;\r
190 UINT64 End;\r
191 UINT64 Length;\r
192} PCI_WINDOW;\r
193\r
194typedef struct {\r
195 PCI_WINDOW PciWindow32;\r
196 PCI_WINDOW PciWindow64;\r
197} FIRMWARE_DATA;\r
198\r
199#pragma pack()\r
200\r
201\r
202STATIC\r
203EFI_STATUS\r
204EFIAPI\r
205PopulateFwData(\r
206 OUT FIRMWARE_DATA *FwData\r
207 )\r
208{\r
57c0beb6 209 EFI_STATUS Status;\r
210 UINTN NumDesc;\r
211 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;\r
212\r
213 Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);\r
214 if (Status == EFI_SUCCESS) {\r
215 UINT64 NonMmio32MaxExclTop;\r
216 UINT64 Mmio32MinBase;\r
217 UINT64 Mmio32MaxExclTop;\r
218 UINTN CurDesc;\r
219\r
220 Status = EFI_UNSUPPORTED;\r
221\r
222 NonMmio32MaxExclTop = 0;\r
223 Mmio32MinBase = BASE_4GB;\r
224 Mmio32MaxExclTop = 0;\r
225\r
226 for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {\r
227 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
228 UINT64 ExclTop;\r
229\r
230 Desc = &AllDesc[CurDesc];\r
231 ExclTop = Desc->BaseAddress + Desc->Length;\r
232\r
233 if (ExclTop <= BASE_4GB) {\r
234 switch (Desc->GcdMemoryType) {\r
235 case EfiGcdMemoryTypeNonExistent:\r
236 break;\r
237\r
238 case EfiGcdMemoryTypeReserved:\r
239 case EfiGcdMemoryTypeSystemMemory:\r
240 if (NonMmio32MaxExclTop < ExclTop) {\r
241 NonMmio32MaxExclTop = ExclTop;\r
242 }\r
243 break;\r
244\r
245 case EfiGcdMemoryTypeMemoryMappedIo:\r
246 if (Mmio32MinBase > Desc->BaseAddress) {\r
247 Mmio32MinBase = Desc->BaseAddress;\r
248 }\r
249 if (Mmio32MaxExclTop < ExclTop) {\r
250 Mmio32MaxExclTop = ExclTop;\r
251 }\r
252 break;\r
253\r
254 default:\r
255 ASSERT(0);\r
256 }\r
257 }\r
258 }\r
259\r
260 if (Mmio32MinBase < NonMmio32MaxExclTop) {\r
261 Mmio32MinBase = NonMmio32MaxExclTop;\r
262 }\r
263\r
264 if (Mmio32MinBase < Mmio32MaxExclTop) {\r
265 FwData->PciWindow32.Base = Mmio32MinBase;\r
266 FwData->PciWindow32.End = Mmio32MaxExclTop - 1;\r
267 FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;\r
268\r
269 FwData->PciWindow64.Base = 0;\r
270 FwData->PciWindow64.End = 0;\r
271 FwData->PciWindow64.Length = 0;\r
272\r
273 Status = EFI_SUCCESS;\r
274 }\r
275\r
276 FreePool (AllDesc);\r
277 }\r
278\r
279 DEBUG ((\r
280 DEBUG_INFO,\r
281 "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
282 FwData->PciWindow32.Base,\r
283 FwData->PciWindow32.End,\r
284 FwData->PciWindow32.Length\r
285 ));\r
286 DEBUG ((\r
287 DEBUG_INFO,\r
288 "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
289 FwData->PciWindow64.Base,\r
290 FwData->PciWindow64.End,\r
291 FwData->PciWindow64.Length\r
292 ));\r
293\r
294 return Status;\r
253a2ea7 295}\r
296\r
297\r
298STATIC\r
299EFI_STATUS\r
300EFIAPI\r
301QemuInstallAcpiSsdtTable (\r
302 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
303 IN VOID *AcpiTableBuffer,\r
304 IN UINTN AcpiTableBufferSize,\r
305 OUT UINTN *TableKey\r
306 )\r
307{\r
308 EFI_STATUS Status;\r
309 FIRMWARE_DATA *FwData;\r
310\r
311 Status = EFI_OUT_OF_RESOURCES;\r
312\r
313 FwData = AllocateReservedPool (sizeof (*FwData));\r
314 if (FwData != NULL) {\r
315 UINTN SsdtSize;\r
316 UINT8 *Ssdt;\r
317\r
318 SsdtSize = AcpiTableBufferSize + 17;\r
319 Ssdt = AllocatePool (SsdtSize);\r
320\r
321 if (Ssdt != NULL) {\r
322 Status = PopulateFwData (FwData);\r
323\r
324 if (Status == EFI_SUCCESS) {\r
325 UINT8 *SsdtPtr;\r
326\r
327 SsdtPtr = Ssdt;\r
328\r
329 CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);\r
330 SsdtPtr += AcpiTableBufferSize;\r
331\r
332 //\r
333 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"\r
334 //\r
335 *(SsdtPtr++) = 0x5B; // ExtOpPrefix\r
336 *(SsdtPtr++) = 0x80; // OpRegionOp\r
337 *(SsdtPtr++) = 'F';\r
338 *(SsdtPtr++) = 'W';\r
339 *(SsdtPtr++) = 'D';\r
340 *(SsdtPtr++) = 'T';\r
341 *(SsdtPtr++) = 0x00; // SystemMemory\r
342 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
343\r
344 //\r
345 // no virtual addressing yet, take the four least significant bytes\r
346 //\r
347 CopyMem(SsdtPtr, &FwData, 4);\r
348 SsdtPtr += 4;\r
349\r
350 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
351\r
352 *(UINT32*) SsdtPtr = sizeof (*FwData);\r
353 SsdtPtr += 4;\r
354\r
26af9aca 355 ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);\r
356 ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;\r
253a2ea7 357 Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);\r
358 }\r
359\r
360 FreePool(Ssdt);\r
361 }\r
362\r
363 if (Status != EFI_SUCCESS) {\r
364 FreePool(FwData);\r
365 }\r
366 }\r
367\r
368 return Status;\r
369}\r
370\r
371\r
522203de 372EFI_STATUS\r
373EFIAPI\r
374QemuInstallAcpiTable (\r
375 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
376 IN VOID *AcpiTableBuffer,\r
377 IN UINTN AcpiTableBufferSize,\r
378 OUT UINTN *TableKey\r
379 )\r
380{\r
255b4184 381 EFI_ACPI_DESCRIPTION_HEADER *Hdr;\r
382 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;\r
383\r
384 Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;\r
385 switch (Hdr->Signature) {\r
386 case EFI_ACPI_1_0_APIC_SIGNATURE:\r
387 TableInstallFunction = QemuInstallAcpiMadtTable;\r
388 break;\r
253a2ea7 389 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:\r
390 TableInstallFunction = QemuInstallAcpiSsdtTable;\r
391 break;\r
255b4184 392 default:\r
393 TableInstallFunction = InstallAcpiTable;\r
394 }\r
395\r
396 return TableInstallFunction (\r
522203de 397 AcpiProtocol,\r
398 AcpiTableBuffer,\r
399 AcpiTableBufferSize,\r
400 TableKey\r
401 );\r
402}\r
403\r