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