]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmfPkg: AcpiPlatformDxe: exclude RSD PTR from QEMU's fw_cfg payload
[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
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
255b4184 19#include <Library/BaseMemoryLib.h>\r
20#include <Library/MemoryAllocationLib.h>\r
522203de 21#include <Library/QemuFwCfgLib.h>\r
57c0beb6 22#include <Library/DxeServicesTableLib.h>\r
498f7d8d 23#include <Library/PcdLib.h>\r
24#include <IndustryStandard/Acpi.h>\r
522203de 25\r
26BOOLEAN\r
27QemuDetected (\r
28 VOID\r
29 )\r
30{\r
31 if (!QemuFwCfgIsAvailable ()) {\r
32 return FALSE;\r
33 }\r
34\r
35 return TRUE;\r
36}\r
37\r
38\r
498f7d8d 39STATIC\r
40UINTN\r
41CountBits16 (\r
42 UINT16 Mask\r
43 )\r
44{\r
45 //\r
46 // For all N >= 1, N bits are enough to represent the number of bits set\r
47 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),\r
48 // the maximum number of possibly set bits increases by one, while the\r
49 // representable maximum doubles.\r
50 //\r
51 Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);\r
52 Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);\r
53 Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);\r
54 Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);\r
55\r
56 return Mask;\r
57}\r
58\r
59\r
255b4184 60STATIC\r
61EFI_STATUS\r
62EFIAPI\r
63QemuInstallAcpiMadtTable (\r
64 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
65 IN VOID *AcpiTableBuffer,\r
66 IN UINTN AcpiTableBufferSize,\r
67 OUT UINTN *TableKey\r
68 )\r
69{\r
498f7d8d 70 UINTN CpuCount;\r
71 UINTN PciLinkIsoCount;\r
72 UINTN NewBufferSize;\r
73 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;\r
74 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;\r
75 EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;\r
76 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;\r
77 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;\r
78 VOID *Ptr;\r
79 UINTN Loop;\r
80 EFI_STATUS Status;\r
81\r
82 ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
255b4184 83\r
84 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
498f7d8d 85 CpuCount = QemuFwCfgRead16 ();\r
86 ASSERT (CpuCount >= 1);\r
255b4184 87\r
88 //\r
498f7d8d 89 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset\r
90 // corresponds to the union of all possible interrupt assignments for the LNKA,\r
91 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.\r
255b4184 92 //\r
498f7d8d 93 PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
255b4184 94\r
498f7d8d 95 NewBufferSize = 1 * sizeof (*Madt) +\r
96 CpuCount * sizeof (*LocalApic) +\r
97 1 * sizeof (*IoApic) +\r
98 (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
99 1 * sizeof (*LocalApicNmi);\r
255b4184 100\r
498f7d8d 101 Madt = AllocatePool (NewBufferSize);\r
102 if (Madt == NULL) {\r
103 return EFI_OUT_OF_RESOURCES;\r
104 }\r
105\r
ff8dd312 106 CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
107 Madt->Header.Length = (UINT32) NewBufferSize;\r
498f7d8d 108 Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
109 Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;\r
110 Ptr = Madt + 1;\r
111\r
112 LocalApic = Ptr;\r
113 for (Loop = 0; Loop < CpuCount; ++Loop) {\r
114 LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
115 LocalApic->Length = sizeof (*LocalApic);\r
ff8dd312 116 LocalApic->AcpiProcessorId = (UINT8) Loop;\r
117 LocalApic->ApicId = (UINT8) Loop;\r
498f7d8d 118 LocalApic->Flags = 1; // enabled\r
119 ++LocalApic;\r
120 }\r
121 Ptr = LocalApic;\r
122\r
123 IoApic = Ptr;\r
124 IoApic->Type = EFI_ACPI_1_0_IO_APIC;\r
125 IoApic->Length = sizeof (*IoApic);\r
ff8dd312 126 IoApic->IoApicId = (UINT8) CpuCount;\r
498f7d8d 127 IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;\r
128 IoApic->IoApicAddress = 0xFEC00000;\r
129 IoApic->SystemVectorBase = 0x00000000;\r
130 Ptr = IoApic + 1;\r
255b4184 131\r
132 //\r
498f7d8d 133 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure\r
255b4184 134 //\r
498f7d8d 135 Iso = Ptr;\r
136 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
137 Iso->Length = sizeof (*Iso);\r
138 Iso->Bus = 0x00; // ISA\r
139 Iso->Source = 0x00; // IRQ0\r
140 Iso->GlobalSystemInterruptVector = 0x00000002;\r
141 Iso->Flags = 0x0000; // Conforms to specs of the bus\r
142 ++Iso;\r
255b4184 143\r
498f7d8d 144 //\r
145 // Set Level-tiggered, Active High for all possible PCI link targets.\r
146 //\r
147 for (Loop = 0; Loop < 16; ++Loop) {\r
148 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {\r
149 continue;\r
150 }\r
151 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
152 Iso->Length = sizeof (*Iso);\r
153 Iso->Bus = 0x00; // ISA\r
ff8dd312 154 Iso->Source = (UINT8) Loop;\r
155 Iso->GlobalSystemInterruptVector = (UINT32) Loop;\r
498f7d8d 156 Iso->Flags = 0x000D; // Level-tiggered, Active High\r
157 ++Iso;\r
158 }\r
159 ASSERT (\r
ff8dd312 160 (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==\r
498f7d8d 161 1 + PciLinkIsoCount\r
162 );\r
163 Ptr = Iso;\r
164\r
165 LocalApicNmi = Ptr;\r
166 LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;\r
167 LocalApicNmi->Length = sizeof (*LocalApicNmi);\r
168 LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors\r
169 //\r
170 // polarity and trigger mode of the APIC I/O input signals conform to the\r
171 // specifications of the bus\r
172 //\r
173 LocalApicNmi->Flags = 0x0000;\r
174 //\r
175 // Local APIC interrupt input LINTn to which NMI is connected.\r
176 //\r
177 LocalApicNmi->LocalApicInti = 0x01;\r
178 Ptr = LocalApicNmi + 1;\r
255b4184 179\r
ff8dd312 180 ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
498f7d8d 181 Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
255b4184 182\r
498f7d8d 183 FreePool (Madt);\r
255b4184 184\r
185 return Status;\r
186}\r
187\r
188\r
253a2ea7 189#pragma pack(1)\r
190\r
191typedef struct {\r
192 UINT64 Base;\r
193 UINT64 End;\r
194 UINT64 Length;\r
195} PCI_WINDOW;\r
196\r
197typedef struct {\r
198 PCI_WINDOW PciWindow32;\r
199 PCI_WINDOW PciWindow64;\r
200} FIRMWARE_DATA;\r
201\r
14430c55 202typedef struct {\r
3d80db51 203 UINT8 BytePrefix;\r
204 UINT8 ByteValue;\r
205} AML_BYTE;\r
206\r
207typedef struct {\r
208 UINT8 NameOp;\r
209 UINT8 RootChar;\r
210 UINT8 NameChar[4];\r
211 UINT8 PackageOp;\r
212 UINT8 PkgLength;\r
213 UINT8 NumElements;\r
214 AML_BYTE Pm1aCntSlpTyp;\r
215 AML_BYTE Pm1bCntSlpTyp;\r
216 AML_BYTE Reserved[2];\r
14430c55 217} SYSTEM_STATE_PACKAGE;\r
218\r
253a2ea7 219#pragma pack()\r
220\r
221\r
222STATIC\r
223EFI_STATUS\r
224EFIAPI\r
225PopulateFwData(\r
226 OUT FIRMWARE_DATA *FwData\r
227 )\r
228{\r
57c0beb6 229 EFI_STATUS Status;\r
230 UINTN NumDesc;\r
231 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;\r
232\r
233 Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);\r
234 if (Status == EFI_SUCCESS) {\r
235 UINT64 NonMmio32MaxExclTop;\r
236 UINT64 Mmio32MinBase;\r
237 UINT64 Mmio32MaxExclTop;\r
238 UINTN CurDesc;\r
239\r
240 Status = EFI_UNSUPPORTED;\r
241\r
242 NonMmio32MaxExclTop = 0;\r
243 Mmio32MinBase = BASE_4GB;\r
244 Mmio32MaxExclTop = 0;\r
245\r
246 for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {\r
247 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
248 UINT64 ExclTop;\r
249\r
250 Desc = &AllDesc[CurDesc];\r
251 ExclTop = Desc->BaseAddress + Desc->Length;\r
252\r
2662fa8d 253 if (ExclTop <= (UINT64) PcdGet32 (PcdOvmfFdBaseAddress)) {\r
57c0beb6 254 switch (Desc->GcdMemoryType) {\r
255 case EfiGcdMemoryTypeNonExistent:\r
256 break;\r
257\r
258 case EfiGcdMemoryTypeReserved:\r
259 case EfiGcdMemoryTypeSystemMemory:\r
260 if (NonMmio32MaxExclTop < ExclTop) {\r
261 NonMmio32MaxExclTop = ExclTop;\r
262 }\r
263 break;\r
264\r
265 case EfiGcdMemoryTypeMemoryMappedIo:\r
266 if (Mmio32MinBase > Desc->BaseAddress) {\r
267 Mmio32MinBase = Desc->BaseAddress;\r
268 }\r
269 if (Mmio32MaxExclTop < ExclTop) {\r
270 Mmio32MaxExclTop = ExclTop;\r
271 }\r
272 break;\r
273\r
274 default:\r
275 ASSERT(0);\r
276 }\r
277 }\r
278 }\r
279\r
280 if (Mmio32MinBase < NonMmio32MaxExclTop) {\r
281 Mmio32MinBase = NonMmio32MaxExclTop;\r
282 }\r
283\r
284 if (Mmio32MinBase < Mmio32MaxExclTop) {\r
285 FwData->PciWindow32.Base = Mmio32MinBase;\r
286 FwData->PciWindow32.End = Mmio32MaxExclTop - 1;\r
287 FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;\r
288\r
289 FwData->PciWindow64.Base = 0;\r
290 FwData->PciWindow64.End = 0;\r
291 FwData->PciWindow64.Length = 0;\r
292\r
293 Status = EFI_SUCCESS;\r
294 }\r
295\r
296 FreePool (AllDesc);\r
297 }\r
298\r
299 DEBUG ((\r
300 DEBUG_INFO,\r
301 "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
302 FwData->PciWindow32.Base,\r
303 FwData->PciWindow32.End,\r
304 FwData->PciWindow32.Length\r
305 ));\r
306 DEBUG ((\r
307 DEBUG_INFO,\r
308 "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
309 FwData->PciWindow64.Base,\r
310 FwData->PciWindow64.End,\r
311 FwData->PciWindow64.Length\r
312 ));\r
313\r
314 return Status;\r
253a2ea7 315}\r
316\r
317\r
14430c55 318STATIC\r
319VOID\r
320EFIAPI\r
321GetSuspendStates (\r
322 UINTN *SuspendToRamSize,\r
323 SYSTEM_STATE_PACKAGE *SuspendToRam,\r
324 UINTN *SuspendToDiskSize,\r
325 SYSTEM_STATE_PACKAGE *SuspendToDisk\r
326 )\r
327{\r
328 STATIC CONST SYSTEM_STATE_PACKAGE Template = {\r
329 0x08, // NameOp\r
330 '\\', // RootChar\r
331 { '_', 'S', 'x', '_' }, // NameChar[4]\r
332 0x12, // PackageOp\r
3d80db51 333 0x0A, // PkgLength\r
334 0x04, // NumElements\r
335 { 0x0A, 0x00 }, // Pm1aCntSlpTyp\r
336 { 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it\r
337 { // Reserved[2]\r
338 { 0x0A, 0x00 },\r
339 { 0x0A, 0x00 }\r
340 }\r
14430c55 341 };\r
342 RETURN_STATUS Status;\r
343 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
344 UINTN FwCfgSize;\r
345 UINT8 SystemStates[6];\r
346\r
347 //\r
348 // configure defaults\r
349 //\r
350 *SuspendToRamSize = sizeof Template;\r
351 CopyMem (SuspendToRam, &Template, sizeof Template);\r
3d80db51 352 SuspendToRam->NameChar[2] = '3'; // S3\r
353 SuspendToRam->Pm1aCntSlpTyp.ByteValue = 1; // PIIX4: STR\r
14430c55 354\r
355 *SuspendToDiskSize = sizeof Template;\r
356 CopyMem (SuspendToDisk, &Template, sizeof Template);\r
3d80db51 357 SuspendToDisk->NameChar[2] = '4'; // S4\r
358 SuspendToDisk->Pm1aCntSlpTyp.ByteValue = 2; // PIIX4: POSCL\r
14430c55 359\r
360 //\r
361 // check for overrides\r
362 //\r
363 Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);\r
364 if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {\r
365 DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));\r
366 return;\r
367 }\r
368 QemuFwCfgSelectItem (FwCfgItem);\r
369 QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);\r
370\r
371 //\r
372 // Each byte corresponds to a system state. In each byte, the MSB tells us\r
373 // whether the given state is enabled. If so, the three LSBs specify the\r
374 // value to be written to the PM control register's SUS_TYP bits.\r
375 //\r
376 if (SystemStates[3] & BIT7) {\r
3d80db51 377 SuspendToRam->Pm1aCntSlpTyp.ByteValue =\r
378 SystemStates[3] & (BIT2 | BIT1 | BIT0);\r
379 DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n",\r
380 SuspendToRam->Pm1aCntSlpTyp.ByteValue));\r
14430c55 381 } else {\r
382 *SuspendToRamSize = 0;\r
383 DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));\r
384 }\r
385\r
386 if (SystemStates[4] & BIT7) {\r
3d80db51 387 SuspendToDisk->Pm1aCntSlpTyp.ByteValue =\r
388 SystemStates[4] & (BIT2 | BIT1 | BIT0);\r
389 DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n",\r
390 SuspendToDisk->Pm1aCntSlpTyp.ByteValue));\r
14430c55 391 } else {\r
392 *SuspendToDiskSize = 0;\r
393 DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));\r
394 }\r
395}\r
396\r
397\r
253a2ea7 398STATIC\r
399EFI_STATUS\r
400EFIAPI\r
401QemuInstallAcpiSsdtTable (\r
402 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
403 IN VOID *AcpiTableBuffer,\r
404 IN UINTN AcpiTableBufferSize,\r
405 OUT UINTN *TableKey\r
406 )\r
407{\r
408 EFI_STATUS Status;\r
409 FIRMWARE_DATA *FwData;\r
410\r
411 Status = EFI_OUT_OF_RESOURCES;\r
412\r
413 FwData = AllocateReservedPool (sizeof (*FwData));\r
414 if (FwData != NULL) {\r
14430c55 415 UINTN SuspendToRamSize;\r
416 SYSTEM_STATE_PACKAGE SuspendToRam;\r
417 UINTN SuspendToDiskSize;\r
418 SYSTEM_STATE_PACKAGE SuspendToDisk;\r
419 UINTN SsdtSize;\r
420 UINT8 *Ssdt;\r
421\r
422 GetSuspendStates (&SuspendToRamSize, &SuspendToRam,\r
423 &SuspendToDiskSize, &SuspendToDisk);\r
424 SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;\r
253a2ea7 425 Ssdt = AllocatePool (SsdtSize);\r
426\r
427 if (Ssdt != NULL) {\r
428 Status = PopulateFwData (FwData);\r
429\r
430 if (Status == EFI_SUCCESS) {\r
431 UINT8 *SsdtPtr;\r
432\r
433 SsdtPtr = Ssdt;\r
434\r
435 CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);\r
436 SsdtPtr += AcpiTableBufferSize;\r
437\r
438 //\r
439 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"\r
440 //\r
441 *(SsdtPtr++) = 0x5B; // ExtOpPrefix\r
442 *(SsdtPtr++) = 0x80; // OpRegionOp\r
443 *(SsdtPtr++) = 'F';\r
444 *(SsdtPtr++) = 'W';\r
445 *(SsdtPtr++) = 'D';\r
446 *(SsdtPtr++) = 'T';\r
447 *(SsdtPtr++) = 0x00; // SystemMemory\r
448 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
449\r
450 //\r
451 // no virtual addressing yet, take the four least significant bytes\r
452 //\r
453 CopyMem(SsdtPtr, &FwData, 4);\r
454 SsdtPtr += 4;\r
455\r
456 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
457\r
458 *(UINT32*) SsdtPtr = sizeof (*FwData);\r
459 SsdtPtr += 4;\r
460\r
14430c55 461 //\r
462 // add suspend system states\r
463 //\r
464 CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);\r
465 SsdtPtr += SuspendToRamSize;\r
466 CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);\r
467 SsdtPtr += SuspendToDiskSize;\r
468\r
26af9aca 469 ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);\r
470 ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;\r
253a2ea7 471 Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);\r
472 }\r
473\r
474 FreePool(Ssdt);\r
475 }\r
476\r
477 if (Status != EFI_SUCCESS) {\r
478 FreePool(FwData);\r
479 }\r
480 }\r
481\r
482 return Status;\r
483}\r
484\r
485\r
522203de 486EFI_STATUS\r
487EFIAPI\r
488QemuInstallAcpiTable (\r
489 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
490 IN VOID *AcpiTableBuffer,\r
491 IN UINTN AcpiTableBufferSize,\r
492 OUT UINTN *TableKey\r
493 )\r
494{\r
255b4184 495 EFI_ACPI_DESCRIPTION_HEADER *Hdr;\r
496 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;\r
497\r
498 Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;\r
499 switch (Hdr->Signature) {\r
500 case EFI_ACPI_1_0_APIC_SIGNATURE:\r
501 TableInstallFunction = QemuInstallAcpiMadtTable;\r
502 break;\r
253a2ea7 503 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:\r
504 TableInstallFunction = QemuInstallAcpiSsdtTable;\r
505 break;\r
255b4184 506 default:\r
507 TableInstallFunction = InstallAcpiTable;\r
508 }\r
509\r
510 return TableInstallFunction (\r
522203de 511 AcpiProtocol,\r
512 AcpiTableBuffer,\r
513 AcpiTableBufferSize,\r
514 TableKey\r
515 );\r
516}\r
517\r
96bbdbc8 518\r
374df8fc
LE
519/**\r
520 Check if an array of bytes starts with an RSD PTR structure.\r
521\r
522 Checksum is ignored.\r
523\r
524 @param[in] Buffer The array to check.\r
525\r
526 @param[in] Size Number of bytes in Buffer.\r
527\r
528 @param[out] RsdpSize If the function returns EFI_SUCCESS, this parameter\r
529 contains the size of the detected RSD PTR structure.\r
530\r
531 @retval EFI_SUCCESS RSD PTR structure detected at the beginning of\r
532 Buffer, and its advertised size does not exceed\r
533 Size.\r
534\r
535 @retval EFI_PROTOCOL_ERROR RSD PTR structure detected at the beginning of\r
536 Buffer, but it has inconsistent size.\r
537\r
538 @retval EFI_NOT_FOUND RSD PTR structure not found.\r
539\r
540**/\r
541\r
542STATIC\r
543EFI_STATUS\r
544CheckRsdp (\r
545 IN CONST VOID *Buffer,\r
546 IN UINTN Size,\r
547 OUT UINTN *RsdpSize\r
548 )\r
549{\r
550 CONST UINT64 *Signature;\r
551 CONST EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp1;\r
552 CONST EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp2;\r
553\r
554 if (Size < sizeof *Signature) {\r
555 return EFI_NOT_FOUND;\r
556 }\r
557 Signature = Buffer;\r
558\r
559 if (*Signature != EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_POINTER_SIGNATURE) {\r
560 return EFI_NOT_FOUND;\r
561 }\r
562\r
563 //\r
564 // Signature found -- from this point on we can only report\r
565 // EFI_PROTOCOL_ERROR or EFI_SUCCESS.\r
566 //\r
567 if (Size < sizeof *Rsdp1) {\r
568 return EFI_PROTOCOL_ERROR;\r
569 }\r
570 Rsdp1 = Buffer;\r
571\r
572 if (Rsdp1->Reserved == 0) {\r
573 //\r
574 // ACPI 1.0 doesn't include the Length field\r
575 //\r
576 *RsdpSize = sizeof *Rsdp1;\r
577 return EFI_SUCCESS;\r
578 }\r
579\r
580 if (Size < sizeof *Rsdp2) {\r
581 return EFI_PROTOCOL_ERROR;\r
582 }\r
583 Rsdp2 = Buffer;\r
584\r
585 if (Size < Rsdp2->Length || Rsdp2->Length < sizeof *Rsdp2) {\r
586 return EFI_PROTOCOL_ERROR;\r
587 }\r
588\r
589 *RsdpSize = Rsdp2->Length;\r
590 return EFI_SUCCESS;\r
591}\r
592\r
2d1fe950
LE
593//\r
594// We'll be saving the keys of installed tables so that we can roll them back\r
595// in case of failure. 128 tables should be enough for anyone (TM).\r
596//\r
597#define INSTALLED_TABLES_MAX 128\r
598\r
96bbdbc8 599/**\r
2d1fe950 600 Download one ACPI table data file from QEMU and interpret it.\r
96bbdbc8 601\r
2d1fe950
LE
602 @param[in] FwCfgFile The NUL-terminated name of the fw_cfg file to\r
603 download and interpret.\r
96bbdbc8 604\r
2d1fe950 605 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
96bbdbc8 606\r
2d1fe950
LE
607 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
608 elements, allocated by the caller. On output,\r
609 the function will have stored (appended) the\r
610 AcpiProtocol-internal keys of the ACPI tables\r
611 that the function has installed from the fw_cfg\r
612 file. The array reflects installed tables even\r
613 if the function returns with an error.\r
96bbdbc8 614\r
2d1fe950
LE
615 @param[in,out] NumInstalled On input, the number of entries already used in\r
616 InstalledKey; it must be in [0,\r
617 INSTALLED_TABLES_MAX] inclusive. On output, the\r
618 parameter is updated to the new cumulative count\r
619 of the keys stored in InstalledKey; the value\r
620 reflects installed tables even if the function\r
621 returns with an error.\r
96bbdbc8 622\r
2d1fe950
LE
623 @retval EFI_INVALID_PARAMETER NumInstalled is outside the allowed range on\r
624 input.\r
96bbdbc8 625\r
2d1fe950 626 @retval EFI_UNSUPPORTED Firmware configuration is unavailable.\r
96bbdbc8 627\r
2d1fe950
LE
628 @retval EFI_NOT_FOUND The host doesn't export the requested fw_cfg\r
629 file.\r
96bbdbc8 630\r
2d1fe950
LE
631 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or no more room in\r
632 InstalledKey.\r
633\r
634 @retval EFI_PROTOCOL_ERROR Found truncated or invalid ACPI table header\r
635 in the fw_cfg contents.\r
96bbdbc8 636\r
2d1fe950
LE
637 @return Status codes returned by\r
638 AcpiProtocol->InstallAcpiTable().\r
639\r
640**/\r
641\r
642STATIC\r
96bbdbc8 643EFI_STATUS\r
96bbdbc8 644InstallQemuLinkedTables (\r
2d1fe950
LE
645 IN CONST CHAR8 *FwCfgFile,\r
646 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
647 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
648 IN OUT INT32 *NumInstalled\r
96bbdbc8
LE
649 )\r
650{\r
651 EFI_STATUS Status;\r
652 FIRMWARE_CONFIG_ITEM TablesFile;\r
653 UINTN TablesFileSize;\r
654 UINT8 *Tables;\r
96bbdbc8 655 UINTN Processed;\r
96bbdbc8 656\r
2d1fe950
LE
657 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {\r
658 return EFI_INVALID_PARAMETER;\r
659 }\r
660\r
661 Status = QemuFwCfgFindFile (FwCfgFile, &TablesFile, &TablesFileSize);\r
96bbdbc8 662 if (EFI_ERROR (Status)) {\r
2d1fe950
LE
663 DEBUG ((EFI_D_ERROR, "%a: \"%a\" unavailable: %r\n", __FUNCTION__,\r
664 FwCfgFile, Status));\r
96bbdbc8
LE
665 return Status;\r
666 }\r
667\r
668 Tables = AllocatePool (TablesFileSize);\r
669 if (Tables == NULL) {\r
670 return EFI_OUT_OF_RESOURCES;\r
671 }\r
672\r
673 QemuFwCfgSelectItem (TablesFile);\r
674 QemuFwCfgReadBytes (TablesFileSize, Tables);\r
675\r
96bbdbc8 676 Processed = 0;\r
96bbdbc8
LE
677 while (Processed < TablesFileSize) {\r
678 UINTN Remaining;\r
374df8fc 679 UINTN RsdpSize;\r
96bbdbc8
LE
680 EFI_ACPI_DESCRIPTION_HEADER *Probe;\r
681\r
682 Remaining = TablesFileSize - Processed;\r
374df8fc
LE
683\r
684 //\r
685 // See if we're looking at an RSD PTR structure.\r
686 //\r
687 RsdpSize = 0;\r
688 Status = CheckRsdp (Tables + Processed, Remaining, &RsdpSize);\r
689 if (Status == EFI_PROTOCOL_ERROR) {\r
690 //\r
691 // RSD PTR found but its size is inconsistent; abort processing. (Note\r
692 // that "RSD PTR found" excludes the NUL-padding case by definition.)\r
693 //\r
694 break;\r
695 }\r
696 if (!EFI_ERROR (Status)) {\r
697 //\r
698 // Consistent RSD PTR found, skip it.\r
699 //\r
700 DEBUG ((EFI_D_VERBOSE, "%a: \"%a\" offset 0x%016Lx: RSD PTR "\r
701 "Length=0x%08x\n", __FUNCTION__, FwCfgFile, (UINT64)Processed,\r
702 (UINT32)RsdpSize));\r
703 Processed += RsdpSize;\r
704 continue;\r
705 }\r
706 ASSERT (Status == EFI_NOT_FOUND);\r
707\r
708 //\r
709 // What we're looking at is not an RSD PTR structure; attempt to parse it\r
710 // as an ACPI table.\r
711 //\r
96bbdbc8
LE
712 if (Remaining < sizeof *Probe) {\r
713 Status = EFI_PROTOCOL_ERROR;\r
714 break;\r
715 }\r
716\r
717 Probe = (EFI_ACPI_DESCRIPTION_HEADER *) (Tables + Processed);\r
718 if (Remaining < Probe->Length || Probe->Length < sizeof *Probe) {\r
719 Status = EFI_PROTOCOL_ERROR;\r
720 break;\r
721 }\r
722\r
2d1fe950 723 DEBUG ((EFI_D_VERBOSE, "%a: \"%a\" offset 0x%016Lx:"\r
96bbdbc8 724 " Signature=\"%-4.4a\" Length=0x%08x\n",\r
2d1fe950 725 __FUNCTION__, FwCfgFile, (UINT64) Processed,\r
96bbdbc8
LE
726 (CONST CHAR8 *) &Probe->Signature, Probe->Length));\r
727\r
728 //\r
729 // skip automatically handled "root" tables: RSDT, XSDT\r
730 //\r
731 if (Probe->Signature !=\r
732 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE &&\r
733 Probe->Signature !=\r
734 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
2d1fe950 735 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
96bbdbc8
LE
736 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
737 __FUNCTION__, INSTALLED_TABLES_MAX));\r
738 Status = EFI_OUT_OF_RESOURCES;\r
739 break;\r
740 }\r
741\r
742 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, Probe,\r
2d1fe950 743 Probe->Length, &InstalledKey[*NumInstalled]);\r
96bbdbc8
LE
744 if (EFI_ERROR (Status)) {\r
745 DEBUG ((EFI_D_ERROR,\r
2d1fe950
LE
746 "%a: failed to install table \"%-4.4a\" at \"%a\" offset 0x%Lx: "\r
747 "%r\n", __FUNCTION__, (CONST CHAR8 *)&Probe->Signature, FwCfgFile,\r
748 (UINT64) Processed, Status));\r
96bbdbc8
LE
749 break;\r
750 }\r
751\r
2d1fe950 752 ++*NumInstalled;\r
96bbdbc8
LE
753 }\r
754\r
755 Processed += Probe->Length;\r
756 }\r
757\r
758 //\r
759 // NUL-padding at the end is accepted\r
760 //\r
761 if (Status == EFI_PROTOCOL_ERROR) {\r
762 UINTN ErrorLocation;\r
763\r
764 ErrorLocation = Processed;\r
765 while (Processed < TablesFileSize && Tables[Processed] == '\0') {\r
766 ++Processed;\r
767 }\r
768 if (Processed < TablesFileSize) {\r
769 DEBUG ((EFI_D_ERROR, "%a: truncated or invalid ACPI table header at "\r
2d1fe950
LE
770 "\"%a\" offset 0x%Lx\n", __FUNCTION__, FwCfgFile,\r
771 (UINT64)ErrorLocation));\r
96bbdbc8
LE
772 }\r
773 }\r
774\r
775 if (Processed == TablesFileSize) {\r
96bbdbc8
LE
776 Status = EFI_SUCCESS;\r
777 } else {\r
778 ASSERT (EFI_ERROR (Status));\r
2d1fe950 779 }\r
96bbdbc8 780\r
2d1fe950
LE
781 FreePool (Tables);\r
782 return Status;\r
783}\r
784\r
785/**\r
786 Download all ACPI table data files from QEMU and interpret them.\r
787\r
788 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
789\r
790 @retval EFI_UNSUPPORTED Firmware configuration is unavailable.\r
791\r
792 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
793 files.\r
794\r
795 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
796 INSTALLED_TABLES_MAX tables found.\r
797\r
798 @retval EFI_PROTOCOL_ERROR Found truncated or invalid ACPI table header\r
799 in the fw_cfg contents.\r
800\r
801 @return Status codes returned by\r
802 AcpiProtocol->InstallAcpiTable().\r
803\r
804**/\r
805\r
806EFI_STATUS\r
807EFIAPI\r
808InstallAllQemuLinkedTables (\r
809 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
810 )\r
811{\r
812 UINTN *InstalledKey;\r
813 INT32 Installed;\r
814 EFI_STATUS Status;\r
815\r
816 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
817 if (InstalledKey == NULL) {\r
818 return EFI_OUT_OF_RESOURCES;\r
819 }\r
820 Installed = 0;\r
821\r
822 Status = InstallQemuLinkedTables ("etc/acpi/tables", AcpiProtocol,\r
823 InstalledKey, &Installed);\r
824 if (EFI_ERROR (Status)) {\r
825 ASSERT (Status != EFI_INVALID_PARAMETER);\r
96bbdbc8
LE
826 //\r
827 // Roll back partial installation.\r
828 //\r
829 while (Installed > 0) {\r
830 --Installed;\r
831 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
832 }\r
2d1fe950
LE
833 } else {\r
834 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
96bbdbc8
LE
835 }\r
836\r
837 FreePool (InstalledKey);\r
96bbdbc8
LE
838 return Status;\r
839}\r