1 /** @file
2   OVMF ACPI QEMU support
3 
4   Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
5 
6   Copyright (C) 2012-2014, Red Hat, Inc.
7 
8   SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include "AcpiPlatform.h"
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/MemoryAllocationLib.h>
15 #include <Library/QemuFwCfgLib.h>
16 #include <Library/DxeServicesTableLib.h>
17 #include <Library/PcdLib.h>
18 #include <Library/OrderedCollectionLib.h>
19 #include <IndustryStandard/Acpi.h>
20 
21 BOOLEAN
QemuDetected(VOID)22 QemuDetected (
23   VOID
24   )
25 {
26   if (!QemuFwCfgIsAvailable ()) {
27     return FALSE;
28   }
29 
30   return TRUE;
31 }
32 
33 
34 STATIC
35 UINTN
CountBits16(UINT16 Mask)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
QemuInstallAcpiMadtTable(IN EFI_ACPI_TABLE_PROTOCOL * AcpiProtocol,IN VOID * AcpiTableBuffer,IN UINTN AcpiTableBufferSize,OUT UINTN * TableKey)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
PopulateFwData(OUT FIRMWARE_DATA * FwData)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
GetSuspendStates(UINTN * SuspendToRamSize,SYSTEM_STATE_PACKAGE * SuspendToRam,UINTN * SuspendToDiskSize,SYSTEM_STATE_PACKAGE * SuspendToDisk)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
QemuInstallAcpiSsdtTable(IN EFI_ACPI_TABLE_PROTOCOL * AcpiProtocol,IN VOID * AcpiTableBuffer,IN UINTN AcpiTableBufferSize,OUT UINTN * TableKey)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
QemuInstallAcpiTable(IN EFI_ACPI_TABLE_PROTOCOL * AcpiProtocol,IN VOID * AcpiTableBuffer,IN UINTN AcpiTableBufferSize,OUT UINTN * TableKey)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 }
512