]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
OvmfPkg: AcpiPlatformDxe: enable PCI IO and MMIO while fetching QEMU tables
[mirror_edk2.git] / OvmfPkg / AcpiPlatformDxe / QemuFwCfgAcpi.c
CommitLineData
14b0faad
JJ
1/** @file\r
2 OVMF ACPI support using QEMU's fw-cfg interface\r
3\r
4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>\r
5 Copyright (C) 2012-2014, Red Hat, Inc.\r
6\r
7 This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution. The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11\r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14\r
15**/\r
16\r
17#include "AcpiPlatform.h"\r
18#include "QemuLoader.h"\r
19#include <Library/BaseMemoryLib.h>\r
20#include <Library/MemoryAllocationLib.h>\r
21#include <Library/QemuFwCfgLib.h>\r
22#include <Library/DxeServicesTableLib.h>\r
23#include <Library/PcdLib.h>\r
24#include <Library/OrderedCollectionLib.h>\r
25#include <IndustryStandard/Acpi.h>\r
26\r
27\r
28//\r
29// The user structure for the ordered collection that will track the fw_cfg\r
30// blobs under processing.\r
31//\r
32typedef struct {\r
33 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
34 // blob. This is the ordering / search\r
35 // key.\r
36 UINTN Size; // The number of bytes in this blob.\r
37 UINT8 *Base; // Pointer to the blob data.\r
38 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
39 // only contain data that is directly\r
40 // part of ACPI tables.\r
41} BLOB;\r
42\r
43\r
44/**\r
45 Compare a standalone key against a user structure containing an embedded key.\r
46\r
47 @param[in] StandaloneKey Pointer to the bare key.\r
48\r
49 @param[in] UserStruct Pointer to the user structure with the embedded\r
50 key.\r
51\r
52 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
53\r
54 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
55\r
56 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
57**/\r
58STATIC\r
59INTN\r
60EFIAPI\r
61BlobKeyCompare (\r
62 IN CONST VOID *StandaloneKey,\r
63 IN CONST VOID *UserStruct\r
64 )\r
65{\r
66 CONST BLOB *Blob;\r
67\r
68 Blob = UserStruct;\r
69 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
70}\r
71\r
72\r
73/**\r
74 Comparator function for two user structures.\r
75\r
76 @param[in] UserStruct1 Pointer to the first user structure.\r
77\r
78 @param[in] UserStruct2 Pointer to the second user structure.\r
79\r
80 @retval <0 If UserStruct1 compares less than UserStruct2.\r
81\r
82 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
83\r
84 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
85**/\r
86STATIC\r
87INTN\r
88EFIAPI\r
89BlobCompare (\r
90 IN CONST VOID *UserStruct1,\r
91 IN CONST VOID *UserStruct2\r
92 )\r
93{\r
94 CONST BLOB *Blob1;\r
95\r
96 Blob1 = UserStruct1;\r
97 return BlobKeyCompare (Blob1->File, UserStruct2);\r
98}\r
99\r
100\r
101/**\r
102 Process a QEMU_LOADER_ALLOCATE command.\r
103\r
104 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.\r
105\r
106 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
107 structures created thus far.\r
108\r
109 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
110 allocated for the blob contents, and the\r
111 contents have been saved. A BLOB object (user\r
112 structure) has been allocated from pool memory,\r
113 referencing the blob contents. The BLOB user\r
114 structure has been linked into Tracker.\r
115\r
116 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
117 Allocate, or the Allocate command references a\r
118 file that is already known by Tracker.\r
119\r
120 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
121 Allocate.\r
122\r
123 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
124\r
125 @return Error codes from QemuFwCfgFindFile() and\r
126 gBS->AllocatePages().\r
127**/\r
128STATIC\r
129EFI_STATUS\r
130EFIAPI\r
131ProcessCmdAllocate (\r
132 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
133 IN OUT ORDERED_COLLECTION *Tracker\r
134 )\r
135{\r
136 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
137 UINTN FwCfgSize;\r
138 EFI_STATUS Status;\r
139 UINTN NumPages;\r
140 EFI_PHYSICAL_ADDRESS Address;\r
141 BLOB *Blob;\r
142\r
143 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
144 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
145 return EFI_PROTOCOL_ERROR;\r
146 }\r
147\r
148 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
149 DEBUG ((EFI_D_ERROR, "%a: unsupported alignment 0x%x\n", __FUNCTION__,\r
150 Allocate->Alignment));\r
151 return EFI_UNSUPPORTED;\r
152 }\r
153\r
154 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
155 if (EFI_ERROR (Status)) {\r
156 DEBUG ((EFI_D_ERROR, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__,\r
157 Allocate->File, Status));\r
158 return Status;\r
159 }\r
160\r
161 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
162 Address = 0xFFFFFFFF;\r
163 Status = gBS->AllocatePages (AllocateMaxAddress, EfiACPIMemoryNVS, NumPages,\r
164 &Address);\r
165 if (EFI_ERROR (Status)) {\r
166 return Status;\r
167 }\r
168\r
169 Blob = AllocatePool (sizeof *Blob);\r
170 if (Blob == NULL) {\r
171 Status = EFI_OUT_OF_RESOURCES;\r
172 goto FreePages;\r
173 }\r
174 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
175 Blob->Size = FwCfgSize;\r
176 Blob->Base = (VOID *)(UINTN)Address;\r
177 Blob->HostsOnlyTableData = TRUE;\r
178\r
179 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
180 if (Status == RETURN_ALREADY_STARTED) {\r
181 DEBUG ((EFI_D_ERROR, "%a: duplicated file \"%a\"\n", __FUNCTION__,\r
182 Allocate->File));\r
183 Status = EFI_PROTOCOL_ERROR;\r
184 }\r
185 if (EFI_ERROR (Status)) {\r
186 goto FreeBlob;\r
187 }\r
188\r
189 QemuFwCfgSelectItem (FwCfgItem);\r
190 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
191 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
192\r
193 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
194 "Address=0x%Lx\n", __FUNCTION__, Allocate->File, Allocate->Alignment,\r
195 Allocate->Zone, (UINT64)Blob->Size, (UINT64)(UINTN)Blob->Base));\r
196 return EFI_SUCCESS;\r
197\r
198FreeBlob:\r
199 FreePool (Blob);\r
200\r
201FreePages:\r
202 gBS->FreePages (Address, NumPages);\r
203\r
204 return Status;\r
205}\r
206\r
207\r
208/**\r
209 Process a QEMU_LOADER_ADD_POINTER command.\r
210\r
211 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
212\r
213 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
214 structures created thus far.\r
215\r
216 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
217 AddPointer, or the AddPointer command references\r
218 a file unknown to Tracker, or the pointer to\r
219 relocate has invalid location, size, or value, or\r
220 the relocated pointer value is not representable\r
221 in the given pointer size.\r
222\r
223 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
224 been relocated.\r
225**/\r
226STATIC\r
227EFI_STATUS\r
228EFIAPI\r
229ProcessCmdAddPointer (\r
230 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
231 IN CONST ORDERED_COLLECTION *Tracker\r
232 )\r
233{\r
234 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
235 BLOB *Blob, *Blob2;\r
236 UINT8 *PointerField;\r
237 UINT64 PointerValue;\r
238\r
239 if (AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||\r
240 AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
241 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
242 return EFI_PROTOCOL_ERROR;\r
243 }\r
244\r
245 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
246 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
247 if (TrackerEntry == NULL || TrackerEntry2 == NULL) {\r
248 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
249 __FUNCTION__, AddPointer->PointerFile, AddPointer->PointeeFile));\r
250 return EFI_PROTOCOL_ERROR;\r
251 }\r
252\r
253 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
254 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
255 if ((AddPointer->PointerSize != 1 && AddPointer->PointerSize != 2 &&\r
256 AddPointer->PointerSize != 4 && AddPointer->PointerSize != 8) ||\r
257 Blob->Size < AddPointer->PointerSize ||\r
258 Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset) {\r
259 DEBUG ((EFI_D_ERROR, "%a: invalid pointer location or size in \"%a\"\n",\r
260 __FUNCTION__, AddPointer->PointerFile));\r
261 return EFI_PROTOCOL_ERROR;\r
262 }\r
263\r
264 PointerField = Blob->Base + AddPointer->PointerOffset;\r
265 PointerValue = 0;\r
266 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
267 if (PointerValue >= Blob2->Size) {\r
268 DEBUG ((EFI_D_ERROR, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__,\r
269 AddPointer->PointerFile));\r
270 return EFI_PROTOCOL_ERROR;\r
271 }\r
272\r
273 //\r
274 // The memory allocation system ensures that the address of the byte past the\r
275 // last byte of any allocated object is expressible (no wraparound).\r
276 //\r
277 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
278\r
279 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
280 if (RShiftU64 (\r
281 RShiftU64 (PointerValue, AddPointer->PointerSize * 8 - 1), 1) != 0) {\r
282 DEBUG ((EFI_D_ERROR, "%a: relocated pointer value unrepresentable in "\r
283 "\"%a\"\n", __FUNCTION__, AddPointer->PointerFile));\r
284 return EFI_PROTOCOL_ERROR;\r
285 }\r
286\r
287 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
288\r
289 DEBUG ((EFI_D_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
290 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,\r
291 AddPointer->PointerFile, AddPointer->PointeeFile,\r
292 AddPointer->PointerOffset, AddPointer->PointerSize));\r
293 return EFI_SUCCESS;\r
294}\r
295\r
296\r
297/**\r
298 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
299\r
300 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
301\r
302 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
303 structures created thus far.\r
304\r
305 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
306 AddChecksum, or the AddChecksum command\r
307 references a file unknown to Tracker, or the\r
308 range to checksum is invalid.\r
309\r
310 @retval EFI_SUCCESS The requested range has been checksummed.\r
311**/\r
312STATIC\r
313EFI_STATUS\r
314EFIAPI\r
315ProcessCmdAddChecksum (\r
316 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
317 IN CONST ORDERED_COLLECTION *Tracker\r
318 )\r
319{\r
320 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
321 BLOB *Blob;\r
322\r
323 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
324 DEBUG ((EFI_D_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
325 return EFI_PROTOCOL_ERROR;\r
326 }\r
327\r
328 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
329 if (TrackerEntry == NULL) {\r
330 DEBUG ((EFI_D_ERROR, "%a: invalid blob reference \"%a\"\n", __FUNCTION__,\r
331 AddChecksum->File));\r
332 return EFI_PROTOCOL_ERROR;\r
333 }\r
334\r
335 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
336 if (Blob->Size <= AddChecksum->ResultOffset ||\r
337 Blob->Size < AddChecksum->Length ||\r
338 Blob->Size - AddChecksum->Length < AddChecksum->Start) {\r
339 DEBUG ((EFI_D_ERROR, "%a: invalid checksum range in \"%a\"\n",\r
340 __FUNCTION__, AddChecksum->File));\r
341 return EFI_PROTOCOL_ERROR;\r
342 }\r
343\r
344 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
345 Blob->Base + AddChecksum->Start,\r
346 AddChecksum->Length\r
347 );\r
348 DEBUG ((EFI_D_VERBOSE, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
349 "Length=0x%x\n", __FUNCTION__, AddChecksum->File,\r
350 AddChecksum->ResultOffset, AddChecksum->Start, AddChecksum->Length));\r
351 return EFI_SUCCESS;\r
352}\r
353\r
354\r
355//\r
356// We'll be saving the keys of installed tables so that we can roll them back\r
357// in case of failure. 128 tables should be enough for anyone (TM).\r
358//\r
359#define INSTALLED_TABLES_MAX 128\r
360\r
361/**\r
362 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
363 array is an ACPI table, and if so, install it.\r
364\r
365 This function assumes that the entire QEMU linker/loader command file has\r
366 been processed successfuly in a prior first pass.\r
367\r
368 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
369\r
370 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
371 structures.\r
372\r
373 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
374\r
375 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
376 elements, allocated by the caller. On output,\r
377 the function will have stored (appended) the\r
378 AcpiProtocol-internal key of the ACPI table that\r
379 the function has installed, if the AddPointer\r
380 command identified an ACPI table that is\r
381 different from RSDT and XSDT.\r
382\r
383 @param[in,out] NumInstalled On input, the number of entries already used in\r
384 InstalledKey; it must be in [0,\r
385 INSTALLED_TABLES_MAX] inclusive. On output, the\r
386 parameter is incremented if the AddPointer\r
387 command identified an ACPI table that is\r
388 different from RSDT and XSDT.\r
389\r
390 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
391 input.\r
392\r
393 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
394 table different from RSDT and XSDT, but there\r
395 was no more room in InstalledKey.\r
396\r
397 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI\r
398 table different from RSDT and XSDT has been\r
399 installed (reflected by InstalledKey and\r
400 NumInstalled), or RSDT or XSDT has been\r
401 identified but not installed, or the fw_cfg\r
402 blob pointed-into by AddPointer has been\r
403 marked as hosting something else than just\r
404 direct ACPI table contents.\r
405\r
406 @return Error codes returned by\r
407 AcpiProtocol->InstallAcpiTable().\r
408**/\r
409STATIC\r
410EFI_STATUS\r
411EFIAPI\r
412Process2ndPassCmdAddPointer (\r
413 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
414 IN CONST ORDERED_COLLECTION *Tracker,\r
415 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
416 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
417 IN OUT INT32 *NumInstalled\r
418 )\r
419{\r
420 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
421 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
422 CONST BLOB *Blob;\r
423 BLOB *Blob2;\r
424 CONST UINT8 *PointerField;\r
425 UINT64 PointerValue;\r
426 UINTN Blob2Remaining;\r
427 UINTN TableSize;\r
428 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
429 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
430 EFI_STATUS Status;\r
431\r
432 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {\r
433 return EFI_INVALID_PARAMETER;\r
434 }\r
435\r
436 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
437 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
438 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
439 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
440 PointerField = Blob->Base + AddPointer->PointerOffset;\r
441 PointerValue = 0;\r
442 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
443\r
444 //\r
445 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
446 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
447 //\r
448 Blob2Remaining = (UINTN)Blob2->Base;\r
449 ASSERT(PointerValue >= Blob2Remaining);\r
450 Blob2Remaining += Blob2->Size;\r
451 ASSERT (PointerValue < Blob2Remaining);\r
452\r
453 Blob2Remaining -= (UINTN) PointerValue;\r
454 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
455 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,\r
456 PointerValue, (UINT64)Blob2Remaining));\r
457\r
458 TableSize = 0;\r
459\r
460 //\r
461 // To make our job simple, the FACS has a custom header. Sigh.\r
462 //\r
463 if (sizeof *Facs <= Blob2Remaining) {\r
464 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
465\r
466 if (Facs->Length >= sizeof *Facs &&\r
467 Facs->Length <= Blob2Remaining &&\r
468 Facs->Signature ==\r
469 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {\r
470 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
471 (CONST CHAR8 *)&Facs->Signature, Facs->Length));\r
472 TableSize = Facs->Length;\r
473 }\r
474 }\r
475\r
476 //\r
477 // check for the uniform tables\r
478 //\r
479 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {\r
480 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
481\r
482 if (Header->Length >= sizeof *Header &&\r
483 Header->Length <= Blob2Remaining &&\r
484 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {\r
485 //\r
486 // This looks very much like an ACPI table from QEMU:\r
487 // - Length field consistent with both ACPI and containing blob size\r
488 // - checksum is correct\r
489 //\r
490 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",\r
491 (CONST CHAR8 *)&Header->Signature, Header->Length));\r
492 TableSize = Header->Length;\r
493\r
494 //\r
495 // Skip RSDT and XSDT because those are handled by\r
496 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
497 if (Header->Signature ==\r
498 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||\r
499 Header->Signature ==\r
500 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {\r
501 return EFI_SUCCESS;\r
502 }\r
503 }\r
504 }\r
505\r
506 if (TableSize == 0) {\r
507 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
508 Blob2->HostsOnlyTableData = FALSE;\r
509 return EFI_SUCCESS;\r
510 }\r
511\r
512 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
513 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",\r
514 __FUNCTION__, INSTALLED_TABLES_MAX));\r
515 return EFI_OUT_OF_RESOURCES;\r
516 }\r
517\r
518 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,\r
519 (VOID *)(UINTN)PointerValue, TableSize,\r
520 &InstalledKey[*NumInstalled]);\r
521 if (EFI_ERROR (Status)) {\r
522 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,\r
523 Status));\r
524 return Status;\r
525 }\r
526 ++*NumInstalled;\r
527 return EFI_SUCCESS;\r
528}\r
529\r
530\r
531/**\r
532 Download, process, and install ACPI table data from the QEMU loader\r
533 interface.\r
534\r
535 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
536\r
537 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
538 loader command with unsupported parameters\r
539 has been found.\r
540\r
541 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
542 files.\r
543\r
544 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
545 INSTALLED_TABLES_MAX tables found.\r
546\r
547 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
548\r
549 @return Status codes returned by\r
550 AcpiProtocol->InstallAcpiTable().\r
551\r
552**/\r
553EFI_STATUS\r
554EFIAPI\r
f186536b 555InstallQemuFwCfgTables (\r
14b0faad
JJ
556 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
557 )\r
558{\r
559 EFI_STATUS Status;\r
560 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
561 UINTN FwCfgSize;\r
562 QEMU_LOADER_ENTRY *LoaderStart;\r
563 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
8f35eb92
LE
564 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
565 UINTN OriginalPciAttributesCount;\r
14b0faad
JJ
566 ORDERED_COLLECTION *Tracker;\r
567 UINTN *InstalledKey;\r
568 INT32 Installed;\r
569 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
570\r
571 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
572 if (EFI_ERROR (Status)) {\r
573 return Status;\r
574 }\r
575 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
576 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
577 __FUNCTION__, (UINT64)FwCfgSize));\r
578 return EFI_PROTOCOL_ERROR;\r
579 }\r
580\r
581 LoaderStart = AllocatePool (FwCfgSize);\r
582 if (LoaderStart == NULL) {\r
583 return EFI_OUT_OF_RESOURCES;\r
584 }\r
8f35eb92 585 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);\r
14b0faad
JJ
586 QemuFwCfgSelectItem (FwCfgItem);\r
587 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
8f35eb92 588 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);\r
14b0faad
JJ
589 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
590\r
591 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
592 if (Tracker == NULL) {\r
593 Status = EFI_OUT_OF_RESOURCES;\r
594 goto FreeLoader;\r
595 }\r
596\r
597 //\r
598 // first pass: process the commands\r
599 //\r
600 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
601 switch (LoaderEntry->Type) {\r
602 case QemuLoaderCmdAllocate:\r
603 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);\r
604 break;\r
605\r
606 case QemuLoaderCmdAddPointer:\r
607 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
608 Tracker);\r
609 break;\r
610\r
611 case QemuLoaderCmdAddChecksum:\r
612 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,\r
613 Tracker);\r
614 break;\r
615\r
616 default:\r
617 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",\r
618 __FUNCTION__, LoaderEntry->Type));\r
619 break;\r
620 }\r
621\r
622 if (EFI_ERROR (Status)) {\r
623 goto FreeTracker;\r
624 }\r
625 }\r
626\r
627 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
628 if (InstalledKey == NULL) {\r
629 Status = EFI_OUT_OF_RESOURCES;\r
630 goto FreeTracker;\r
631 }\r
632\r
633 //\r
634 // second pass: identify and install ACPI tables\r
635 //\r
636 Installed = 0;\r
637 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
638 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
639 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,\r
640 Tracker, AcpiProtocol, InstalledKey, &Installed);\r
641 if (EFI_ERROR (Status)) {\r
642 break;\r
643 }\r
644 }\r
645 }\r
646\r
647 if (EFI_ERROR (Status)) {\r
648 //\r
649 // roll back partial installation\r
650 //\r
651 while (Installed > 0) {\r
652 --Installed;\r
653 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
654 }\r
655 } else {\r
656 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
657 }\r
658\r
659 FreePool (InstalledKey);\r
660\r
661FreeTracker:\r
662 //\r
663 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
664 // place only if we're exiting with success and the blob hosts data that is\r
665 // not directly part of some ACPI table.\r
666 //\r
667 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
668 TrackerEntry = TrackerEntry2) {\r
669 VOID *UserStruct;\r
670 BLOB *Blob;\r
671\r
672 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
673 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
674 Blob = UserStruct;\r
675\r
676 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
677 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,\r
678 Blob->File));\r
679 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
680 }\r
681 FreePool (Blob);\r
682 }\r
683 OrderedCollectionUninit (Tracker);\r
684\r
685FreeLoader:\r
686 FreePool (LoaderStart);\r
687\r
688 return Status;\r
689}\r