]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmgPkg: QemuFwCfgLib: export QEMU_FW_CFG_FNAME_SIZE
[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
6 Copyright (C) 2012, Red Hat, Inc.\r
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
LE
518\r
519/**\r
520 Download the ACPI table data file from QEMU and interpret it.\r
521\r
522 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
523\r
524 @retval EFI_UNSUPPORTED Firmware configuration is unavailable.\r
525\r
526 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
527 files.\r
528\r
529 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
530\r
531 @retval EFI_PROTOCOL_ERROR Found truncated or invalid ACPI table header\r
532 in the fw_cfg contents.\r
533\r
534 @return Status codes returned by\r
535 AcpiProtocol->InstallAcpiTable().\r
536\r
537**/\r
538\r
539//\r
540// We'll be saving the keys of installed tables so that we can roll them back\r
541// in case of failure. 128 tables should be enough for anyone (TM).\r
542//\r
543#define INSTALLED_TABLES_MAX 128\r
544\r
545EFI_STATUS\r
546EFIAPI\r
547InstallQemuLinkedTables (\r
548 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
549 )\r
550{\r
551 EFI_STATUS Status;\r
552 FIRMWARE_CONFIG_ITEM TablesFile;\r
553 UINTN TablesFileSize;\r
554 UINT8 *Tables;\r
555 UINTN *InstalledKey;\r
556 UINTN Processed;\r
557 INT32 Installed;\r
558\r
559 Status = QemuFwCfgFindFile ("etc/acpi/tables", &TablesFile, &TablesFileSize);\r
560 if (EFI_ERROR (Status)) {\r
561 DEBUG ((EFI_D_INFO, "%a: \"etc/acpi/tables\" interface unavailable: %r\n",\r
562 __FUNCTION__, Status));\r
563 return Status;\r
564 }\r
565\r
566 Tables = AllocatePool (TablesFileSize);\r
567 if (Tables == NULL) {\r
568 return EFI_OUT_OF_RESOURCES;\r
569 }\r
570\r
571 QemuFwCfgSelectItem (TablesFile);\r
572 QemuFwCfgReadBytes (TablesFileSize, Tables);\r
573\r
574 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
575 if (InstalledKey == NULL) {\r
576 Status = EFI_OUT_OF_RESOURCES;\r
577 goto FreeTables;\r
578 }\r
579\r
580 Processed = 0;\r
581 Installed = 0;\r
582 while (Processed < TablesFileSize) {\r
583 UINTN Remaining;\r
584 EFI_ACPI_DESCRIPTION_HEADER *Probe;\r
585\r
586 Remaining = TablesFileSize - Processed;\r
587 if (Remaining < sizeof *Probe) {\r
588 Status = EFI_PROTOCOL_ERROR;\r
589 break;\r
590 }\r
591\r
592 Probe = (EFI_ACPI_DESCRIPTION_HEADER *) (Tables + Processed);\r
593 if (Remaining < Probe->Length || Probe->Length < sizeof *Probe) {\r
594 Status = EFI_PROTOCOL_ERROR;\r
595 break;\r
596 }\r
597\r
598 DEBUG ((EFI_D_VERBOSE, "%a: offset 0x%016Lx:"\r
599 " Signature=\"%-4.4a\" Length=0x%08x\n",\r
600 __FUNCTION__, (UINT64) Processed,\r
601 (CONST CHAR8 *) &Probe->Signature, Probe->Length));\r
602\r
603 //\r
604 // skip automatically handled "root" tables: RSDT, XSDT\r
605 //\r
606 if (Probe->Signature !=\r
607 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE &&\r
608 Probe->Signature !=\r
609 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
610 if (Installed == INSTALLED_TABLES_MAX) {\r
611 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
612 __FUNCTION__, INSTALLED_TABLES_MAX));\r
613 Status = EFI_OUT_OF_RESOURCES;\r
614 break;\r
615 }\r
616\r
617 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol, Probe,\r
618 Probe->Length, &InstalledKey[Installed]);\r
619 if (EFI_ERROR (Status)) {\r
620 DEBUG ((EFI_D_ERROR,\r
621 "%a: failed to install table \"%-4.4a\" at offset 0x%Lx: %r\n",\r
622 __FUNCTION__, (CONST CHAR8 *) &Probe->Signature, (UINT64) Processed,\r
623 Status));\r
624 break;\r
625 }\r
626\r
627 ++Installed;\r
628 }\r
629\r
630 Processed += Probe->Length;\r
631 }\r
632\r
633 //\r
634 // NUL-padding at the end is accepted\r
635 //\r
636 if (Status == EFI_PROTOCOL_ERROR) {\r
637 UINTN ErrorLocation;\r
638\r
639 ErrorLocation = Processed;\r
640 while (Processed < TablesFileSize && Tables[Processed] == '\0') {\r
641 ++Processed;\r
642 }\r
643 if (Processed < TablesFileSize) {\r
644 DEBUG ((EFI_D_ERROR, "%a: truncated or invalid ACPI table header at "\r
645 "offset 0x%Lx\n", __FUNCTION__, (UINT64) ErrorLocation));\r
646 }\r
647 }\r
648\r
649 if (Processed == TablesFileSize) {\r
650 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
651 Status = EFI_SUCCESS;\r
652 } else {\r
653 ASSERT (EFI_ERROR (Status));\r
654\r
655 //\r
656 // Roll back partial installation.\r
657 //\r
658 while (Installed > 0) {\r
659 --Installed;\r
660 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
661 }\r
662 }\r
663\r
664 FreePool (InstalledKey);\r
665\r
666FreeTables:\r
667 FreePool (Tables);\r
668\r
669 return Status;\r
670}\r