]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/Qemu.c
OvmfPkg/AcpiPlatformDxe: Fix VS2012 IA32 build warning
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / Qemu.c
CommitLineData
522203de 1/** @file\r
2 OVMF ACPI QEMU support\r
3\r
ce883845 4 Copyright (c) 2008 - 2014, 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
a618eaa1 19#include "QemuLoader.h"\r
255b4184 20#include <Library/BaseMemoryLib.h>\r
21#include <Library/MemoryAllocationLib.h>\r
522203de 22#include <Library/QemuFwCfgLib.h>\r
57c0beb6 23#include <Library/DxeServicesTableLib.h>\r
498f7d8d 24#include <Library/PcdLib.h>\r
387536e4 25#include <Library/OrderedCollectionLib.h>\r
498f7d8d 26#include <IndustryStandard/Acpi.h>\r
522203de 27\r
28BOOLEAN\r
29QemuDetected (\r
30 VOID\r
31 )\r
32{\r
33 if (!QemuFwCfgIsAvailable ()) {\r
34 return FALSE;\r
35 }\r
36\r
37 return TRUE;\r
38}\r
39\r
40\r
498f7d8d 41STATIC\r
42UINTN\r
43CountBits16 (\r
44 UINT16 Mask\r
45 )\r
46{\r
47 //\r
48 // For all N >= 1, N bits are enough to represent the number of bits set\r
49 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),\r
50 // the maximum number of possibly set bits increases by one, while the\r
51 // representable maximum doubles.\r
52 //\r
53 Mask = ((Mask & 0xAAAA) >> 1) + (Mask & 0x5555);\r
54 Mask = ((Mask & 0xCCCC) >> 2) + (Mask & 0x3333);\r
55 Mask = ((Mask & 0xF0F0) >> 4) + (Mask & 0x0F0F);\r
56 Mask = ((Mask & 0xFF00) >> 8) + (Mask & 0x00FF);\r
57\r
58 return Mask;\r
59}\r
60\r
61\r
255b4184 62STATIC\r
63EFI_STATUS\r
64EFIAPI\r
65QemuInstallAcpiMadtTable (\r
66 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
67 IN VOID *AcpiTableBuffer,\r
68 IN UINTN AcpiTableBufferSize,\r
69 OUT UINTN *TableKey\r
70 )\r
71{\r
498f7d8d 72 UINTN CpuCount;\r
73 UINTN PciLinkIsoCount;\r
74 UINTN NewBufferSize;\r
75 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;\r
76 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE *LocalApic;\r
77 EFI_ACPI_1_0_IO_APIC_STRUCTURE *IoApic;\r
78 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *Iso;\r
79 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE *LocalApicNmi;\r
80 VOID *Ptr;\r
81 UINTN Loop;\r
82 EFI_STATUS Status;\r
83\r
84 ASSERT (AcpiTableBufferSize >= sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
255b4184 85\r
86 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount);\r
498f7d8d 87 CpuCount = QemuFwCfgRead16 ();\r
88 ASSERT (CpuCount >= 1);\r
255b4184 89\r
90 //\r
498f7d8d 91 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset\r
92 // corresponds to the union of all possible interrupt assignments for the LNKA,\r
93 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.\r
255b4184 94 //\r
498f7d8d 95 PciLinkIsoCount = CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel));\r
255b4184 96\r
498f7d8d 97 NewBufferSize = 1 * sizeof (*Madt) +\r
98 CpuCount * sizeof (*LocalApic) +\r
99 1 * sizeof (*IoApic) +\r
100 (1 + PciLinkIsoCount) * sizeof (*Iso) +\r
101 1 * sizeof (*LocalApicNmi);\r
255b4184 102\r
498f7d8d 103 Madt = AllocatePool (NewBufferSize);\r
104 if (Madt == NULL) {\r
105 return EFI_OUT_OF_RESOURCES;\r
106 }\r
107\r
ff8dd312 108 CopyMem (&(Madt->Header), AcpiTableBuffer, sizeof (EFI_ACPI_DESCRIPTION_HEADER));\r
109 Madt->Header.Length = (UINT32) NewBufferSize;\r
498f7d8d 110 Madt->LocalApicAddress = PcdGet32 (PcdCpuLocalApicBaseAddress);\r
111 Madt->Flags = EFI_ACPI_1_0_PCAT_COMPAT;\r
112 Ptr = Madt + 1;\r
113\r
114 LocalApic = Ptr;\r
115 for (Loop = 0; Loop < CpuCount; ++Loop) {\r
116 LocalApic->Type = EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC;\r
117 LocalApic->Length = sizeof (*LocalApic);\r
ff8dd312 118 LocalApic->AcpiProcessorId = (UINT8) Loop;\r
119 LocalApic->ApicId = (UINT8) Loop;\r
498f7d8d 120 LocalApic->Flags = 1; // enabled\r
121 ++LocalApic;\r
122 }\r
123 Ptr = LocalApic;\r
124\r
125 IoApic = Ptr;\r
126 IoApic->Type = EFI_ACPI_1_0_IO_APIC;\r
127 IoApic->Length = sizeof (*IoApic);\r
ff8dd312 128 IoApic->IoApicId = (UINT8) CpuCount;\r
498f7d8d 129 IoApic->Reserved = EFI_ACPI_RESERVED_BYTE;\r
130 IoApic->IoApicAddress = 0xFEC00000;\r
131 IoApic->SystemVectorBase = 0x00000000;\r
132 Ptr = IoApic + 1;\r
255b4184 133\r
134 //\r
498f7d8d 135 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure\r
255b4184 136 //\r
498f7d8d 137 Iso = Ptr;\r
138 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
139 Iso->Length = sizeof (*Iso);\r
140 Iso->Bus = 0x00; // ISA\r
141 Iso->Source = 0x00; // IRQ0\r
142 Iso->GlobalSystemInterruptVector = 0x00000002;\r
143 Iso->Flags = 0x0000; // Conforms to specs of the bus\r
144 ++Iso;\r
255b4184 145\r
498f7d8d 146 //\r
147 // Set Level-tiggered, Active High for all possible PCI link targets.\r
148 //\r
149 for (Loop = 0; Loop < 16; ++Loop) {\r
150 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel) & (1 << Loop)) == 0) {\r
151 continue;\r
152 }\r
153 Iso->Type = EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE;\r
154 Iso->Length = sizeof (*Iso);\r
155 Iso->Bus = 0x00; // ISA\r
ff8dd312 156 Iso->Source = (UINT8) Loop;\r
157 Iso->GlobalSystemInterruptVector = (UINT32) Loop;\r
498f7d8d 158 Iso->Flags = 0x000D; // Level-tiggered, Active High\r
159 ++Iso;\r
160 }\r
161 ASSERT (\r
ff8dd312 162 (UINTN) (Iso - (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)Ptr) ==\r
498f7d8d 163 1 + PciLinkIsoCount\r
164 );\r
165 Ptr = Iso;\r
166\r
167 LocalApicNmi = Ptr;\r
168 LocalApicNmi->Type = EFI_ACPI_1_0_LOCAL_APIC_NMI;\r
169 LocalApicNmi->Length = sizeof (*LocalApicNmi);\r
170 LocalApicNmi->AcpiProcessorId = 0xFF; // applies to all processors\r
171 //\r
172 // polarity and trigger mode of the APIC I/O input signals conform to the\r
173 // specifications of the bus\r
174 //\r
175 LocalApicNmi->Flags = 0x0000;\r
176 //\r
177 // Local APIC interrupt input LINTn to which NMI is connected.\r
178 //\r
179 LocalApicNmi->LocalApicInti = 0x01;\r
180 Ptr = LocalApicNmi + 1;\r
255b4184 181\r
ff8dd312 182 ASSERT ((UINTN) ((UINT8 *)Ptr - (UINT8 *)Madt) == NewBufferSize);\r
498f7d8d 183 Status = InstallAcpiTable (AcpiProtocol, Madt, NewBufferSize, TableKey);\r
255b4184 184\r
498f7d8d 185 FreePool (Madt);\r
255b4184 186\r
187 return Status;\r
188}\r
189\r
190\r
253a2ea7 191#pragma pack(1)\r
192\r
193typedef struct {\r
194 UINT64 Base;\r
195 UINT64 End;\r
196 UINT64 Length;\r
197} PCI_WINDOW;\r
198\r
199typedef struct {\r
200 PCI_WINDOW PciWindow32;\r
201 PCI_WINDOW PciWindow64;\r
202} FIRMWARE_DATA;\r
203\r
14430c55 204typedef struct {\r
3d80db51 205 UINT8 BytePrefix;\r
206 UINT8 ByteValue;\r
207} AML_BYTE;\r
208\r
209typedef struct {\r
210 UINT8 NameOp;\r
211 UINT8 RootChar;\r
212 UINT8 NameChar[4];\r
213 UINT8 PackageOp;\r
214 UINT8 PkgLength;\r
215 UINT8 NumElements;\r
216 AML_BYTE Pm1aCntSlpTyp;\r
217 AML_BYTE Pm1bCntSlpTyp;\r
218 AML_BYTE Reserved[2];\r
14430c55 219} SYSTEM_STATE_PACKAGE;\r
220\r
253a2ea7 221#pragma pack()\r
222\r
223\r
224STATIC\r
225EFI_STATUS\r
226EFIAPI\r
227PopulateFwData(\r
228 OUT FIRMWARE_DATA *FwData\r
229 )\r
230{\r
57c0beb6 231 EFI_STATUS Status;\r
232 UINTN NumDesc;\r
233 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *AllDesc;\r
234\r
235 Status = gDS->GetMemorySpaceMap (&NumDesc, &AllDesc);\r
236 if (Status == EFI_SUCCESS) {\r
237 UINT64 NonMmio32MaxExclTop;\r
238 UINT64 Mmio32MinBase;\r
239 UINT64 Mmio32MaxExclTop;\r
240 UINTN CurDesc;\r
241\r
242 Status = EFI_UNSUPPORTED;\r
243\r
244 NonMmio32MaxExclTop = 0;\r
245 Mmio32MinBase = BASE_4GB;\r
246 Mmio32MaxExclTop = 0;\r
247\r
248 for (CurDesc = 0; CurDesc < NumDesc; ++CurDesc) {\r
249 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;\r
250 UINT64 ExclTop;\r
251\r
252 Desc = &AllDesc[CurDesc];\r
253 ExclTop = Desc->BaseAddress + Desc->Length;\r
254\r
2662fa8d 255 if (ExclTop <= (UINT64) PcdGet32 (PcdOvmfFdBaseAddress)) {\r
57c0beb6 256 switch (Desc->GcdMemoryType) {\r
257 case EfiGcdMemoryTypeNonExistent:\r
258 break;\r
259\r
260 case EfiGcdMemoryTypeReserved:\r
261 case EfiGcdMemoryTypeSystemMemory:\r
262 if (NonMmio32MaxExclTop < ExclTop) {\r
263 NonMmio32MaxExclTop = ExclTop;\r
264 }\r
265 break;\r
266\r
267 case EfiGcdMemoryTypeMemoryMappedIo:\r
268 if (Mmio32MinBase > Desc->BaseAddress) {\r
269 Mmio32MinBase = Desc->BaseAddress;\r
270 }\r
271 if (Mmio32MaxExclTop < ExclTop) {\r
272 Mmio32MaxExclTop = ExclTop;\r
273 }\r
274 break;\r
275\r
276 default:\r
277 ASSERT(0);\r
278 }\r
279 }\r
280 }\r
281\r
282 if (Mmio32MinBase < NonMmio32MaxExclTop) {\r
283 Mmio32MinBase = NonMmio32MaxExclTop;\r
284 }\r
285\r
286 if (Mmio32MinBase < Mmio32MaxExclTop) {\r
287 FwData->PciWindow32.Base = Mmio32MinBase;\r
288 FwData->PciWindow32.End = Mmio32MaxExclTop - 1;\r
289 FwData->PciWindow32.Length = Mmio32MaxExclTop - Mmio32MinBase;\r
290\r
291 FwData->PciWindow64.Base = 0;\r
292 FwData->PciWindow64.End = 0;\r
293 FwData->PciWindow64.Length = 0;\r
294\r
295 Status = EFI_SUCCESS;\r
296 }\r
297\r
298 FreePool (AllDesc);\r
299 }\r
300\r
301 DEBUG ((\r
302 DEBUG_INFO,\r
303 "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
304 FwData->PciWindow32.Base,\r
305 FwData->PciWindow32.End,\r
306 FwData->PciWindow32.Length\r
307 ));\r
308 DEBUG ((\r
309 DEBUG_INFO,\r
310 "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",\r
311 FwData->PciWindow64.Base,\r
312 FwData->PciWindow64.End,\r
313 FwData->PciWindow64.Length\r
314 ));\r
315\r
316 return Status;\r
253a2ea7 317}\r
318\r
319\r
14430c55 320STATIC\r
321VOID\r
322EFIAPI\r
323GetSuspendStates (\r
324 UINTN *SuspendToRamSize,\r
325 SYSTEM_STATE_PACKAGE *SuspendToRam,\r
326 UINTN *SuspendToDiskSize,\r
327 SYSTEM_STATE_PACKAGE *SuspendToDisk\r
328 )\r
329{\r
330 STATIC CONST SYSTEM_STATE_PACKAGE Template = {\r
331 0x08, // NameOp\r
332 '\\', // RootChar\r
333 { '_', 'S', 'x', '_' }, // NameChar[4]\r
334 0x12, // PackageOp\r
3d80db51 335 0x0A, // PkgLength\r
336 0x04, // NumElements\r
337 { 0x0A, 0x00 }, // Pm1aCntSlpTyp\r
338 { 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it\r
339 { // Reserved[2]\r
340 { 0x0A, 0x00 },\r
341 { 0x0A, 0x00 }\r
342 }\r
14430c55 343 };\r
344 RETURN_STATUS Status;\r
345 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
346 UINTN FwCfgSize;\r
347 UINT8 SystemStates[6];\r
348\r
349 //\r
350 // configure defaults\r
351 //\r
352 *SuspendToRamSize = sizeof Template;\r
353 CopyMem (SuspendToRam, &Template, sizeof Template);\r
3d80db51 354 SuspendToRam->NameChar[2] = '3'; // S3\r
355 SuspendToRam->Pm1aCntSlpTyp.ByteValue = 1; // PIIX4: STR\r
14430c55 356\r
357 *SuspendToDiskSize = sizeof Template;\r
358 CopyMem (SuspendToDisk, &Template, sizeof Template);\r
3d80db51 359 SuspendToDisk->NameChar[2] = '4'; // S4\r
360 SuspendToDisk->Pm1aCntSlpTyp.ByteValue = 2; // PIIX4: POSCL\r
14430c55 361\r
362 //\r
363 // check for overrides\r
364 //\r
365 Status = QemuFwCfgFindFile ("etc/system-states", &FwCfgItem, &FwCfgSize);\r
366 if (Status != RETURN_SUCCESS || FwCfgSize != sizeof SystemStates) {\r
367 DEBUG ((DEBUG_INFO, "ACPI using S3/S4 defaults\n"));\r
368 return;\r
369 }\r
370 QemuFwCfgSelectItem (FwCfgItem);\r
371 QemuFwCfgReadBytes (sizeof SystemStates, SystemStates);\r
372\r
373 //\r
374 // Each byte corresponds to a system state. In each byte, the MSB tells us\r
375 // whether the given state is enabled. If so, the three LSBs specify the\r
376 // value to be written to the PM control register's SUS_TYP bits.\r
377 //\r
378 if (SystemStates[3] & BIT7) {\r
3d80db51 379 SuspendToRam->Pm1aCntSlpTyp.ByteValue =\r
380 SystemStates[3] & (BIT2 | BIT1 | BIT0);\r
381 DEBUG ((DEBUG_INFO, "ACPI S3 value: %d\n",\r
382 SuspendToRam->Pm1aCntSlpTyp.ByteValue));\r
14430c55 383 } else {\r
384 *SuspendToRamSize = 0;\r
385 DEBUG ((DEBUG_INFO, "ACPI S3 disabled\n"));\r
386 }\r
387\r
388 if (SystemStates[4] & BIT7) {\r
3d80db51 389 SuspendToDisk->Pm1aCntSlpTyp.ByteValue =\r
390 SystemStates[4] & (BIT2 | BIT1 | BIT0);\r
391 DEBUG ((DEBUG_INFO, "ACPI S4 value: %d\n",\r
392 SuspendToDisk->Pm1aCntSlpTyp.ByteValue));\r
14430c55 393 } else {\r
394 *SuspendToDiskSize = 0;\r
395 DEBUG ((DEBUG_INFO, "ACPI S4 disabled\n"));\r
396 }\r
397}\r
398\r
399\r
253a2ea7 400STATIC\r
401EFI_STATUS\r
402EFIAPI\r
403QemuInstallAcpiSsdtTable (\r
404 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
405 IN VOID *AcpiTableBuffer,\r
406 IN UINTN AcpiTableBufferSize,\r
407 OUT UINTN *TableKey\r
408 )\r
409{\r
410 EFI_STATUS Status;\r
411 FIRMWARE_DATA *FwData;\r
412\r
413 Status = EFI_OUT_OF_RESOURCES;\r
414\r
415 FwData = AllocateReservedPool (sizeof (*FwData));\r
416 if (FwData != NULL) {\r
14430c55 417 UINTN SuspendToRamSize;\r
418 SYSTEM_STATE_PACKAGE SuspendToRam;\r
419 UINTN SuspendToDiskSize;\r
420 SYSTEM_STATE_PACKAGE SuspendToDisk;\r
421 UINTN SsdtSize;\r
422 UINT8 *Ssdt;\r
423\r
424 GetSuspendStates (&SuspendToRamSize, &SuspendToRam,\r
425 &SuspendToDiskSize, &SuspendToDisk);\r
426 SsdtSize = AcpiTableBufferSize + 17 + SuspendToRamSize + SuspendToDiskSize;\r
253a2ea7 427 Ssdt = AllocatePool (SsdtSize);\r
428\r
429 if (Ssdt != NULL) {\r
430 Status = PopulateFwData (FwData);\r
431\r
432 if (Status == EFI_SUCCESS) {\r
433 UINT8 *SsdtPtr;\r
434\r
435 SsdtPtr = Ssdt;\r
436\r
437 CopyMem (SsdtPtr, AcpiTableBuffer, AcpiTableBufferSize);\r
438 SsdtPtr += AcpiTableBufferSize;\r
439\r
440 //\r
441 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"\r
442 //\r
443 *(SsdtPtr++) = 0x5B; // ExtOpPrefix\r
444 *(SsdtPtr++) = 0x80; // OpRegionOp\r
445 *(SsdtPtr++) = 'F';\r
446 *(SsdtPtr++) = 'W';\r
447 *(SsdtPtr++) = 'D';\r
448 *(SsdtPtr++) = 'T';\r
449 *(SsdtPtr++) = 0x00; // SystemMemory\r
450 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
451\r
452 //\r
453 // no virtual addressing yet, take the four least significant bytes\r
454 //\r
455 CopyMem(SsdtPtr, &FwData, 4);\r
456 SsdtPtr += 4;\r
457\r
458 *(SsdtPtr++) = 0x0C; // DWordPrefix\r
459\r
460 *(UINT32*) SsdtPtr = sizeof (*FwData);\r
461 SsdtPtr += 4;\r
462\r
14430c55 463 //\r
464 // add suspend system states\r
465 //\r
466 CopyMem (SsdtPtr, &SuspendToRam, SuspendToRamSize);\r
467 SsdtPtr += SuspendToRamSize;\r
468 CopyMem (SsdtPtr, &SuspendToDisk, SuspendToDiskSize);\r
469 SsdtPtr += SuspendToDiskSize;\r
470\r
26af9aca 471 ASSERT((UINTN) (SsdtPtr - Ssdt) == SsdtSize);\r
472 ((EFI_ACPI_DESCRIPTION_HEADER *) Ssdt)->Length = (UINT32) SsdtSize;\r
253a2ea7 473 Status = InstallAcpiTable (AcpiProtocol, Ssdt, SsdtSize, TableKey);\r
474 }\r
475\r
476 FreePool(Ssdt);\r
477 }\r
478\r
479 if (Status != EFI_SUCCESS) {\r
480 FreePool(FwData);\r
481 }\r
482 }\r
483\r
484 return Status;\r
485}\r
486\r
487\r
522203de 488EFI_STATUS\r
489EFIAPI\r
490QemuInstallAcpiTable (\r
491 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
492 IN VOID *AcpiTableBuffer,\r
493 IN UINTN AcpiTableBufferSize,\r
494 OUT UINTN *TableKey\r
495 )\r
496{\r
255b4184 497 EFI_ACPI_DESCRIPTION_HEADER *Hdr;\r
498 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction;\r
499\r
500 Hdr = (EFI_ACPI_DESCRIPTION_HEADER*) AcpiTableBuffer;\r
501 switch (Hdr->Signature) {\r
502 case EFI_ACPI_1_0_APIC_SIGNATURE:\r
503 TableInstallFunction = QemuInstallAcpiMadtTable;\r
504 break;\r
253a2ea7 505 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE:\r
506 TableInstallFunction = QemuInstallAcpiSsdtTable;\r
507 break;\r
255b4184 508 default:\r
509 TableInstallFunction = InstallAcpiTable;\r
510 }\r
511\r
512 return TableInstallFunction (\r
522203de 513 AcpiProtocol,\r
514 AcpiTableBuffer,\r
515 AcpiTableBufferSize,\r
516 TableKey\r
517 );\r
518}\r
519\r
96bbdbc8 520\r
387536e4
LE
521//\r
522// The user structure for the ordered collection that will track the fw_cfg\r
523// blobs under processing.\r
524//\r
525typedef struct {\r
526 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
527 // blob. This is the ordering / search\r
528 // key.\r
529 UINTN Size; // The number of bytes in this blob.\r
530 UINT8 *Base; // Pointer to the blob data.\r
531 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
532 // only contain data that is directly\r
533 // part of ACPI tables.\r
534} BLOB;\r
535\r
536\r
537/**\r
538 Compare a standalone key against a user structure containing an embedded key.\r
539\r
540 @param[in] StandaloneKey Pointer to the bare key.\r
541\r
542 @param[in] UserStruct Pointer to the user structure with the embedded\r
543 key.\r
544\r
545 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
546\r
547 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
548\r
549 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
550**/\r
551STATIC\r
552INTN\r
553EFIAPI\r
554BlobKeyCompare (\r
555 IN CONST VOID *StandaloneKey,\r
556 IN CONST VOID *UserStruct\r
557 )\r
558{\r
559 CONST BLOB *Blob;\r
560\r
561 Blob = UserStruct;\r
562 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
563}\r
564\r
565\r
566/**\r
567 Comparator function for two user structures.\r
568\r
569 @param[in] UserStruct1 Pointer to the first user structure.\r
570\r
571 @param[in] UserStruct2 Pointer to the second user structure.\r
572\r
573 @retval <0 If UserStruct1 compares less than UserStruct2.\r
574\r
575 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
576\r
577 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
578**/\r
579STATIC\r
580INTN\r
581EFIAPI\r
582BlobCompare (\r
583 IN CONST VOID *UserStruct1,\r
584 IN CONST VOID *UserStruct2\r
585 )\r
586{\r
587 CONST BLOB *Blob1;\r
588\r
589 Blob1 = UserStruct1;\r
590 return BlobKeyCompare (Blob1->File, UserStruct2);\r
591}\r
592\r
593\r
594/**\r
595 Process a QEMU_LOADER_ALLOCATE command.\r
596\r
597 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.\r
598\r
599 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
600 structures created thus far.\r
601\r
602 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
603 allocated for the blob contents, and the\r
604 contents have been saved. A BLOB object (user\r
605 structure) has been allocated from pool memory,\r
606 referencing the blob contents. The BLOB user\r
607 structure has been linked into Tracker.\r
608\r
609 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
610 Allocate, or the Allocate command references a\r
611 file that is already known by Tracker.\r
612\r
613 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
614 Allocate.\r
615\r
616 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
617\r
618 @return Error codes from QemuFwCfgFindFile() and\r
619 gBS->AllocatePages().\r
620**/\r
621STATIC\r
622EFI_STATUS\r
623EFIAPI\r
624ProcessCmdAllocate (\r
625 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
626 IN OUT ORDERED_COLLECTION *Tracker\r
627 )\r
628{\r
629 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
630 UINTN FwCfgSize;\r
631 EFI_STATUS Status;\r
632 UINTN NumPages;\r
633 EFI_PHYSICAL_ADDRESS Address;\r
634 BLOB *Blob;\r
635\r
636 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
637 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
638 return EFI_PROTOCOL_ERROR;\r
639 }\r
640\r
641 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
642 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,\r
643 Allocate->Alignment));\r
644 return EFI_UNSUPPORTED;\r
645 }\r
646\r
647 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
648 if (EFI_ERROR (Status)) {\r
649 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,\r
650 Allocate->File, Status));\r
651 return Status;\r
652 }\r
653\r
654 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
655 Address = 0xFFFFFFFF;\r
656 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,\r
657 &Address);\r
658 if (EFI_ERROR (Status)) {\r
659 return Status;\r
660 }\r
661\r
662 Blob = AllocatePool (sizeof *Blob);\r
663 if (Blob == NULL) {\r
664 Status = EFI_OUT_OF_RESOURCES;\r
665 goto FreePages;\r
666 }\r
667 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
668 Blob->Size = FwCfgSize;\r
669 Blob->Base = (VOID *)(UINTN)Address;\r
670 Blob->HostsOnlyTableData = TRUE;\r
671\r
672 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
673 if (Status == RETURN_ALREADY_STARTED) {\r
674 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,\r
675 Allocate->File));\r
676 Status = EFI_PROTOCOL_ERROR;\r
677 }\r
678 if (EFI_ERROR (Status)) {\r
679 goto FreeBlob;\r
680 }\r
681\r
682 QemuFwCfgSelectItem (FwCfgItem);\r
683 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
684 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
685\r
686 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
687 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,\r
688 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));\r
689 return EFI_SUCCESS;\r
690\r
691FreeBlob:\r
692 FreePool (Blob);\r
693\r
694FreePages:\r
695 gBS->FreePages (Address, NumPages);\r
696\r
697 return Status;\r
698}\r
699\r
700\r
701/**\r
702 Process a QEMU_LOADER_ADD_POINTER command.\r
703\r
704 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
705\r
706 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
707 structures created thus far.\r
708\r
709 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
710 AddPointer, or the AddPointer command references\r
711 a file unknown to Tracker, or the pointer to\r
712 relocate has invalid location, size, or value, or\r
713 the relocated pointer value is not representable\r
714 in the given pointer size.\r
715\r
716 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
717 been relocated.\r
718**/\r
719STATIC\r
720EFI_STATUS\r
721EFIAPI\r
722ProcessCmdAddPointer (\r
723 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
724 IN CONST ORDERED_COLLECTION *Tracker\r
725 )\r
726{\r
727 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
728 BLOB *Blob, *Blob2;\r
729 UINT8 *PointerField;\r
730 UINT64 PointerValue;\r
731\r
732 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
733 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
734 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
735 return EFI_PROTOCOL_ERROR;\r
736 }\r
737\r
738 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
739 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
740 if (TrackerEntry == NULL || TrackerEntry2 == NULL) {\r
741 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
742 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));\r
743 return EFI_PROTOCOL_ERROR;\r
744 }\r
745\r
746 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
747 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
748 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&\r
749 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||\r
750 Blob->Size < AddPointer->PointerSize ||\r
751 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {\r
752 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
753 __FUNCTION__, AddPointer->PointerFile));\r
754 return EFI_PROTOCOL_ERROR;\r
755 }\r
756\r
757 PointerField = Blob->Base + AddPointer->PointerOffset;\r
758 PointerValue = 0;\r
759 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
760 if (PointerValue >= Blob2->Size) {\r
761 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,\r
762 AddPointer->PointerFile));\r
763 return EFI_PROTOCOL_ERROR;\r
764 }\r
765\r
766 //\r
767 // The memory allocation system ensures that the address of the byte past the\r
768 // last byte of any allocated object is expressible (no wraparound).\r
769 //\r
770 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
771\r
772 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
773 if (RShiftU64 (\r
774 RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) {\r
775 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "\r
776 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));\r
777 return EFI_PROTOCOL_ERROR;\r
778 }\r
779\r
780 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
781\r
782 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
783 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
784 AddPointer->PointerFile, AddPointer->PointeeFile,\r
785 AddPointer->PointerOffset, AddPointer->PointerSize));\r
786 return EFI_SUCCESS;\r
787}\r
788\r
789\r
2d1fe950 790/**\r
387536e4
LE
791 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
792\r
793 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
794\r
795 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
796 structures created thus far.\r
797\r
798 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
799 AddChecksum, or the AddChecksum command\r
800 references a file unknown to Tracker, or the\r
801 range to checksum is invalid.\r
802\r
803 @retval EFI_SUCCESS The requested range has been checksummed.\r
804**/\r
805STATIC\r
806EFI_STATUS\r
807EFIAPI\r
808ProcessCmdAddChecksum (\r
809 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
810 IN CONST ORDERED_COLLECTION *Tracker\r
811 )\r
812{\r
813 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
814 BLOB *Blob;\r
815\r
816 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
817 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
818 return EFI_PROTOCOL_ERROR;\r
819 }\r
820\r
821 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
822 if (TrackerEntry == NULL) {\r
823 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,\r
824 AddChecksum->File));\r
825 return EFI_PROTOCOL_ERROR;\r
826 }\r
827\r
828 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
829 if (Blob->Size <= AddChecksum->ResultOffset ||\r
830 Blob->Size < AddChecksum->Length ||\r
831 Blob->Size - AddChecksum->Length < AddChecksum->Start) {\r
832 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n",\r
833 __FUNCTION__, AddChecksum->File));\r
834 return EFI_PROTOCOL_ERROR;\r
835 }\r
836\r
837 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
838 Blob->Base + AddChecksum->Start,\r
839 AddChecksum->Length\r
840 );\r
841 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
842 "Length=0x%x\n", __FUNCTION__, AddChecksum->File,\r
843 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));\r
844 return EFI_SUCCESS;\r
845}\r
846\r
847\r
848//\r
849// We'll be saving the keys of installed tables so that we can roll them back\r
850// in case of failure. 128 tables should be enough for anyone (TM).\r
851//\r
852#define INSTALLED_TABLES_MAX 128\r
853\r
854/**\r
855 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
856 array is an ACPI table, and if so, install it.\r
857\r
858 This function assumes that the entire QEMU linker/loader command file has\r
859 been processed successfuly in a prior first pass.\r
860\r
861 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
862\r
863 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
864 structures.\r
865\r
866 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
867\r
868 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
869 elements, allocated by the caller. On output,\r
870 the function will have stored (appended) the\r
871 AcpiProtocol-internal key of the ACPI table that\r
872 the function has installed, if the AddPointer\r
873 command identified an ACPI table that is\r
874 different from RSDT and XSDT.\r
875\r
876 @param[in,out] NumInstalled On input, the number of entries already used in\r
877 InstalledKey; it must be in [0,\r
878 INSTALLED_TABLES_MAX] inclusive. On output, the\r
879 parameter is incremented if the AddPointer\r
880 command identified an ACPI table that is\r
881 different from RSDT and XSDT.\r
882\r
883 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
884 input.\r
885\r
886 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
887 table different from RSDT and XSDT, but there\r
888 was no more room in InstalledKey.\r
889\r
890 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI\r
891 table different from RSDT and XSDT has been\r
892 installed (reflected by InstalledKey and\r
893 NumInstalled), or RSDT or XSDT has been\r
894 identified but not installed, or the fw_cfg\r
895 blob pointed-into by AddPointer has been\r
896 marked as hosting something else than just\r
897 direct ACPI table contents.\r
898\r
899 @return Error codes returned by\r
900 AcpiProtocol->InstallAcpiTable().\r
901**/\r
902STATIC\r
903EFI_STATUS\r
904EFIAPI\r
905Process2ndPassCmdAddPointer (\r
906 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
907 IN CONST ORDERED_COLLECTION *Tracker,\r
908 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
909 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
910 IN OUT INT32 *NumInstalled\r
911 )\r
912{\r
913 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
914 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
915 CONST BLOB *Blob;\r
916 BLOB *Blob2;\r
917 CONST UINT8 *PointerField;\r
918 UINT64 PointerValue;\r
919 UINTN Blob2Remaining;\r
920 UINTN TableSize;\r
921 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
922 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
923 EFI_STATUS Status;\r
924\r
925 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {\r
926 return EFI_INVALID_PARAMETER;\r
927 }\r
928\r
929 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
930 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
931 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
932 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
933 PointerField = Blob->Base + AddPointer->PointerOffset;\r
934 PointerValue = 0;\r
935 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
936\r
937 //\r
938 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
939 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
940 //\r
941 Blob2Remaining = (UINTN)Blob2->Base;\r
942 ASSERT(PointerValue >= Blob2Remaining);\r
943 Blob2Remaining += Blob2->Size;\r
944 ASSERT (PointerValue < Blob2Remaining);\r
945\r
ce883845 946 Blob2Remaining -= (UINTN) PointerValue;\r
387536e4
LE
947 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
948 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,\r
949 PointerValue, (UINT64)Blob2Remaining));\r
950\r
951 TableSize = 0;\r
952\r
953 //\r
954 // To make our job simple, the FACS has a custom header. Sigh.\r
955 //\r
956 if (sizeof *Facs <= Blob2Remaining) {\r
957 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
958\r
959 if (Facs->Length >= sizeof *Facs &&\r
960 Facs->Length <= Blob2Remaining &&\r
961 Facs->Signature ==\r
962 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {\r
963 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
964 (CONST CHAR8 *)&Facs->Signature, Facs->Length));\r
965 TableSize = Facs->Length;\r
966 }\r
967 }\r
968\r
969 //\r
970 // check for the uniform tables\r
971 //\r
972 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {\r
973 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
974\r
975 if (Header->Length >= sizeof *Header &&\r
976 Header->Length <= Blob2Remaining &&\r
977 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {\r
978 //\r
979 // This looks very much like an ACPI table from QEMU:\r
980 // - Length field consistent with both ACPI and containing blob size\r
981 // - checksum is correct\r
982 //\r
983 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
984 (CONST CHAR8 *)&Header->Signature, Header->Length));\r
985 TableSize = Header->Length;\r
986\r
987 //\r
988 // Skip RSDT and XSDT because those are handled by\r
989 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
990 if (Header->Signature ==\r
991 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||\r
992 Header->Signature ==\r
993 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
994 return EFI_SUCCESS;\r
995 }\r
996 }\r
997 }\r
998\r
999 if (TableSize == 0) {\r
1000 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
1001 Blob2->HostsOnlyTableData = FALSE;\r
1002 return EFI_SUCCESS;\r
1003 }\r
1004\r
1005 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
1006 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
1007 __FUNCTION__, INSTALLED_TABLES_MAX));\r
1008 return EFI_OUT_OF_RESOURCES;\r
1009 }\r
1010\r
1011 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,\r
1012 (VOID *)(UINTN)PointerValue, TableSize,\r
1013 &InstalledKey[*NumInstalled]);\r
1014 if (EFI_ERROR (Status)) {\r
1015 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,\r
1016 Status));\r
1017 return Status;\r
1018 }\r
1019 ++*NumInstalled;\r
1020 return EFI_SUCCESS;\r
1021}\r
1022\r
1023\r
1024/**\r
1025 Download, process, and install ACPI table data from the QEMU loader\r
1026 interface.\r
2d1fe950
LE
1027\r
1028 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
1029\r
387536e4
LE
1030 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
1031 loader command with unsupported parameters\r
1032 has been found.\r
2d1fe950
LE
1033\r
1034 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
1035 files.\r
1036\r
1037 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
1038 INSTALLED_TABLES_MAX tables found.\r
1039\r
a618eaa1 1040 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
2d1fe950
LE
1041\r
1042 @return Status codes returned by\r
1043 AcpiProtocol->InstallAcpiTable().\r
1044\r
1045**/\r
2d1fe950
LE
1046EFI_STATUS\r
1047EFIAPI\r
1048InstallAllQemuLinkedTables (\r
1049 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
1050 )\r
1051{\r
387536e4
LE
1052 EFI_STATUS Status;\r
1053 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1054 UINTN FwCfgSize;\r
1055 QEMU_LOADER_ENTRY *LoaderStart;\r
1056 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
1057 ORDERED_COLLECTION *Tracker;\r
1058 UINTN *InstalledKey;\r
1059 INT32 Installed;\r
1060 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
1061\r
1062 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
1063 if (EFI_ERROR (Status)) {\r
1064 return Status;\r
1065 }\r
1066 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
1067 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
1068 __FUNCTION__, (UINT64)FwCfgSize));\r
1069 return EFI_PROTOCOL_ERROR;\r
1070 }\r
1071\r
1072 LoaderStart = AllocatePool (FwCfgSize);\r
1073 if (LoaderStart == NULL) {\r
1074 return EFI_OUT_OF_RESOURCES;\r
1075 }\r
1076 QemuFwCfgSelectItem (FwCfgItem);\r
1077 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
1078 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
1079\r
1080 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
1081 if (Tracker == NULL) {\r
1082 Status = EFI_OUT_OF_RESOURCES;\r
1083 goto FreeLoader;\r
1084 }\r
1085\r
1086 //\r
1087 // first pass: process the commands\r
1088 //\r
1089 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1090 switch (LoaderEntry->Type) {\r
1091 case QemuLoaderCmdAllocate:\r
1092 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);\r
1093 break;\r
1094\r
1095 case QemuLoaderCmdAddPointer:\r
1096 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
1097 Tracker);\r
1098 break;\r
1099\r
1100 case QemuLoaderCmdAddChecksum:\r
1101 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,\r
1102 Tracker);\r
1103 break;\r
1104\r
1105 default:\r
1106 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",\r
1107 __FUNCTION__, LoaderEntry->Type));\r
1108 break;\r
1109 }\r
1110\r
1111 if (EFI_ERROR (Status)) {\r
1112 goto FreeTracker;\r
1113 }\r
1114 }\r
1115\r
1116 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
1117 if (InstalledKey == NULL) {\r
1118 Status = EFI_OUT_OF_RESOURCES;\r
1119 goto FreeTracker;\r
1120 }\r
1121\r
1122 //\r
1123 // second pass: identify and install ACPI tables\r
1124 //\r
1125 Installed = 0;\r
1126 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1127 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
1128 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
1129 Tracker, AcpiProtocol, InstalledKey, &Installed);\r
1130 if (EFI_ERROR (Status)) {\r
1131 break;\r
1132 }\r
1133 }\r
1134 }\r
1135\r
1136 if (EFI_ERROR (Status)) {\r
1137 //\r
1138 // roll back partial installation\r
1139 //\r
1140 while (Installed > 0) {\r
1141 --Installed;\r
1142 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
1143 }\r
1144 } else {\r
1145 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
1146 }\r
1147\r
1148 FreePool (InstalledKey);\r
1149\r
1150FreeTracker:\r
1151 //\r
1152 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
1153 // place only if we're exiting with success and the blob hosts data that is\r
1154 // not directly part of some ACPI table.\r
1155 //\r
1156 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
1157 TrackerEntry = TrackerEntry2) {\r
1158 VOID *UserStruct;\r
1159 BLOB *Blob;\r
1160\r
1161 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
1162 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
1163 Blob = UserStruct;\r
1164\r
1165 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
1166 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,\r
1167 Blob->File));\r
1168 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
1169 }\r
1170 FreePool (Blob);\r
1171 }\r
1172 OrderedCollectionUninit (Tracker);\r
1173\r
1174FreeLoader:\r
1175 FreePool (LoaderStart);\r
1176\r
1177 return Status;\r
96bbdbc8 1178}\r