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