]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
OvmfPkg: Apply uncrustify changes
[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
b26f0cf9 7 SPDX-License-Identifier: BSD-2-Clause-Patent\r
14b0faad
JJ
8\r
9**/\r
10\r
cc302b79
LE
11#include <IndustryStandard/Acpi.h> // EFI_ACPI_DESCRIPTION_HEADER\r
12#include <IndustryStandard/QemuLoader.h> // QEMU_LOADER_FNAME_SIZE\r
13#include <Library/BaseLib.h> // AsciiStrCmp()\r
14#include <Library/BaseMemoryLib.h> // CopyMem()\r
15#include <Library/DebugLib.h> // DEBUG()\r
16#include <Library/MemoryAllocationLib.h> // AllocatePool()\r
17#include <Library/OrderedCollectionLib.h> // OrderedCollectionMin()\r
18#include <Library/QemuFwCfgLib.h> // QemuFwCfgFindFile()\r
19#include <Library/QemuFwCfgS3Lib.h> // QemuFwCfgS3Enabled()\r
20#include <Library/UefiBootServicesTableLib.h> // gBS\r
14b0faad 21\r
180f1908 22#include "AcpiPlatform.h"\r
14b0faad
JJ
23\r
24//\r
25// The user structure for the ordered collection that will track the fw_cfg\r
26// blobs under processing.\r
27//\r
28typedef struct {\r
ac0a286f
MK
29 UINT8 File[QEMU_LOADER_FNAME_SIZE]; // NUL-terminated name of the fw_cfg\r
30 // blob. This is the ordering / search\r
31 // key.\r
32 UINTN Size; // The number of bytes in this blob.\r
33 UINT8 *Base; // Pointer to the blob data.\r
34 BOOLEAN HostsOnlyTableData; // TRUE iff the blob has been found to\r
35 // only contain data that is directly\r
36 // part of ACPI tables.\r
14b0faad
JJ
37} BLOB;\r
38\r
14b0faad
JJ
39/**\r
40 Compare a standalone key against a user structure containing an embedded key.\r
41\r
42 @param[in] StandaloneKey Pointer to the bare key.\r
43\r
44 @param[in] UserStruct Pointer to the user structure with the embedded\r
45 key.\r
46\r
47 @retval <0 If StandaloneKey compares less than UserStruct's key.\r
48\r
49 @retval 0 If StandaloneKey compares equal to UserStruct's key.\r
50\r
51 @retval >0 If StandaloneKey compares greater than UserStruct's key.\r
52**/\r
53STATIC\r
54INTN\r
55EFIAPI\r
56BlobKeyCompare (\r
ac0a286f
MK
57 IN CONST VOID *StandaloneKey,\r
58 IN CONST VOID *UserStruct\r
14b0faad
JJ
59 )\r
60{\r
ac0a286f 61 CONST BLOB *Blob;\r
14b0faad
JJ
62\r
63 Blob = UserStruct;\r
64 return AsciiStrCmp (StandaloneKey, (CONST CHAR8 *)Blob->File);\r
65}\r
66\r
14b0faad
JJ
67/**\r
68 Comparator function for two user structures.\r
69\r
70 @param[in] UserStruct1 Pointer to the first user structure.\r
71\r
72 @param[in] UserStruct2 Pointer to the second user structure.\r
73\r
74 @retval <0 If UserStruct1 compares less than UserStruct2.\r
75\r
76 @retval 0 If UserStruct1 compares equal to UserStruct2.\r
77\r
78 @retval >0 If UserStruct1 compares greater than UserStruct2.\r
79**/\r
80STATIC\r
81INTN\r
82EFIAPI\r
83BlobCompare (\r
ac0a286f
MK
84 IN CONST VOID *UserStruct1,\r
85 IN CONST VOID *UserStruct2\r
14b0faad
JJ
86 )\r
87{\r
ac0a286f 88 CONST BLOB *Blob1;\r
14b0faad
JJ
89\r
90 Blob1 = UserStruct1;\r
91 return BlobKeyCompare (Blob1->File, UserStruct2);\r
92}\r
93\r
072060a6
PDJ
94/**\r
95 Comparator function for two opaque pointers, ordering on (unsigned) pointer\r
96 value itself.\r
97 Can be used as both Key and UserStruct comparator.\r
98\r
99 @param[in] Pointer1 First pointer.\r
100\r
101 @param[in] Pointer2 Second pointer.\r
102\r
103 @retval <0 If Pointer1 compares less than Pointer2.\r
104\r
105 @retval 0 If Pointer1 compares equal to Pointer2.\r
106\r
107 @retval >0 If Pointer1 compares greater than Pointer2.\r
108**/\r
109STATIC\r
110INTN\r
111EFIAPI\r
112PointerCompare (\r
ac0a286f
MK
113 IN CONST VOID *Pointer1,\r
114 IN CONST VOID *Pointer2\r
072060a6
PDJ
115 )\r
116{\r
117 if (Pointer1 == Pointer2) {\r
118 return 0;\r
119 }\r
ac0a286f 120\r
072060a6
PDJ
121 if ((UINTN)Pointer1 < (UINTN)Pointer2) {\r
122 return -1;\r
123 }\r
ac0a286f 124\r
072060a6
PDJ
125 return 1;\r
126}\r
127\r
4275f385
LE
128/**\r
129 Comparator function for two ASCII strings. Can be used as both Key and\r
130 UserStruct comparator.\r
131\r
132 This function exists solely so we can avoid casting &AsciiStrCmp to\r
133 ORDERED_COLLECTION_USER_COMPARE and ORDERED_COLLECTION_KEY_COMPARE.\r
134\r
135 @param[in] AsciiString1 Pointer to the first ASCII string.\r
136\r
137 @param[in] AsciiString2 Pointer to the second ASCII string.\r
138\r
139 @return The return value of AsciiStrCmp (AsciiString1, AsciiString2).\r
140**/\r
141STATIC\r
142INTN\r
143EFIAPI\r
144AsciiStringCompare (\r
ac0a286f
MK
145 IN CONST VOID *AsciiString1,\r
146 IN CONST VOID *AsciiString2\r
4275f385
LE
147 )\r
148{\r
149 return AsciiStrCmp (AsciiString1, AsciiString2);\r
150}\r
151\r
4275f385
LE
152/**\r
153 Release the ORDERED_COLLECTION structure populated by\r
154 CollectAllocationsRestrictedTo32Bit() (below).\r
155\r
156 This function may be called by CollectAllocationsRestrictedTo32Bit() itself,\r
157 on the error path.\r
158\r
159 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure to\r
160 release.\r
161**/\r
162STATIC\r
163VOID\r
164ReleaseAllocationsRestrictedTo32Bit (\r
ac0a286f
MK
165 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit\r
166 )\r
4275f385 167{\r
ac0a286f 168 ORDERED_COLLECTION_ENTRY *Entry, *Entry2;\r
4275f385
LE
169\r
170 for (Entry = OrderedCollectionMin (AllocationsRestrictedTo32Bit);\r
171 Entry != NULL;\r
ac0a286f
MK
172 Entry = Entry2)\r
173 {\r
4275f385
LE
174 Entry2 = OrderedCollectionNext (Entry);\r
175 OrderedCollectionDelete (AllocationsRestrictedTo32Bit, Entry, NULL);\r
176 }\r
ac0a286f 177\r
4275f385
LE
178 OrderedCollectionUninit (AllocationsRestrictedTo32Bit);\r
179}\r
180\r
4275f385
LE
181/**\r
182 Iterate over the linker/loader script, and collect the names of the fw_cfg\r
183 blobs that are referenced by QEMU_LOADER_ADD_POINTER.PointeeFile fields, such\r
184 that QEMU_LOADER_ADD_POINTER.PointerSize is less than 8. This means that the\r
185 pointee blob's address will have to be patched into a narrower-than-8 byte\r
186 pointer field, hence the pointee blob must not be allocated from 64-bit\r
187 address space.\r
188\r
189 @param[out] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION structure\r
190 linking (not copying / owning) such\r
191 QEMU_LOADER_ADD_POINTER.PointeeFile\r
192 fields that name the blobs\r
193 restricted from 64-bit allocation.\r
194\r
195 @param[in] LoaderStart Points to the first entry in the\r
196 linker/loader script.\r
197\r
198 @param[in] LoaderEnd Points one past the last entry in\r
199 the linker/loader script.\r
200\r
201 @retval EFI_SUCCESS AllocationsRestrictedTo32Bit has been\r
202 populated.\r
203\r
204 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
205\r
206 @retval EFI_PROTOCOL_ERROR Invalid linker/loader script contents.\r
207**/\r
208STATIC\r
209EFI_STATUS\r
210CollectAllocationsRestrictedTo32Bit (\r
ac0a286f
MK
211 OUT ORDERED_COLLECTION **AllocationsRestrictedTo32Bit,\r
212 IN CONST QEMU_LOADER_ENTRY *LoaderStart,\r
213 IN CONST QEMU_LOADER_ENTRY *LoaderEnd\r
214 )\r
4275f385 215{\r
ac0a286f
MK
216 ORDERED_COLLECTION *Collection;\r
217 CONST QEMU_LOADER_ENTRY *LoaderEntry;\r
218 EFI_STATUS Status;\r
4275f385
LE
219\r
220 Collection = OrderedCollectionInit (AsciiStringCompare, AsciiStringCompare);\r
221 if (Collection == NULL) {\r
222 return EFI_OUT_OF_RESOURCES;\r
223 }\r
224\r
225 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
ac0a286f 226 CONST QEMU_LOADER_ADD_POINTER *AddPointer;\r
4275f385
LE
227\r
228 if (LoaderEntry->Type != QemuLoaderCmdAddPointer) {\r
229 continue;\r
230 }\r
ac0a286f 231\r
4275f385
LE
232 AddPointer = &LoaderEntry->Command.AddPointer;\r
233\r
234 if (AddPointer->PointerSize >= 8) {\r
235 continue;\r
236 }\r
237\r
238 if (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
239 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
240 Status = EFI_PROTOCOL_ERROR;\r
241 goto RollBack;\r
242 }\r
243\r
244 Status = OrderedCollectionInsert (\r
245 Collection,\r
246 NULL, // Entry\r
247 (VOID *)AddPointer->PointeeFile\r
248 );\r
249 switch (Status) {\r
ac0a286f
MK
250 case EFI_SUCCESS:\r
251 DEBUG ((\r
252 DEBUG_VERBOSE,\r
253 "%a: restricting blob \"%a\" from 64-bit allocation\n",\r
254 __FUNCTION__,\r
255 AddPointer->PointeeFile\r
256 ));\r
257 break;\r
258 case EFI_ALREADY_STARTED:\r
259 //\r
260 // The restriction has been recorded already.\r
261 //\r
262 break;\r
263 case EFI_OUT_OF_RESOURCES:\r
264 goto RollBack;\r
265 default:\r
266 ASSERT (FALSE);\r
4275f385
LE
267 }\r
268 }\r
269\r
270 *AllocationsRestrictedTo32Bit = Collection;\r
271 return EFI_SUCCESS;\r
272\r
273RollBack:\r
274 ReleaseAllocationsRestrictedTo32Bit (Collection);\r
275 return Status;\r
276}\r
277\r
14b0faad
JJ
278/**\r
279 Process a QEMU_LOADER_ALLOCATE command.\r
280\r
4275f385
LE
281 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to\r
282 process.\r
14b0faad 283\r
4275f385
LE
284 @param[in,out] Tracker The ORDERED_COLLECTION tracking the\r
285 BLOB user structures created thus\r
286 far.\r
287\r
288 @param[in] AllocationsRestrictedTo32Bit The ORDERED_COLLECTION populated by\r
289 the function\r
290 CollectAllocationsRestrictedTo32Bit,\r
291 naming the fw_cfg blobs that must\r
292 not be allocated from 64-bit address\r
293 space.\r
14b0faad
JJ
294\r
295 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been\r
296 allocated for the blob contents, and the\r
297 contents have been saved. A BLOB object (user\r
298 structure) has been allocated from pool memory,\r
299 referencing the blob contents. The BLOB user\r
300 structure has been linked into Tracker.\r
301\r
302 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
303 Allocate, or the Allocate command references a\r
304 file that is already known by Tracker.\r
305\r
306 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in\r
307 Allocate.\r
308\r
309 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.\r
310\r
311 @return Error codes from QemuFwCfgFindFile() and\r
312 gBS->AllocatePages().\r
313**/\r
314STATIC\r
315EFI_STATUS\r
316EFIAPI\r
317ProcessCmdAllocate (\r
ac0a286f
MK
318 IN CONST QEMU_LOADER_ALLOCATE *Allocate,\r
319 IN OUT ORDERED_COLLECTION *Tracker,\r
320 IN ORDERED_COLLECTION *AllocationsRestrictedTo32Bit\r
14b0faad
JJ
321 )\r
322{\r
ac0a286f
MK
323 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
324 UINTN FwCfgSize;\r
325 EFI_STATUS Status;\r
326 UINTN NumPages;\r
327 EFI_PHYSICAL_ADDRESS Address;\r
328 BLOB *Blob;\r
14b0faad
JJ
329\r
330 if (Allocate->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
70d5086c 331 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
332 return EFI_PROTOCOL_ERROR;\r
333 }\r
334\r
335 if (Allocate->Alignment > EFI_PAGE_SIZE) {\r
ac0a286f
MK
336 DEBUG ((\r
337 DEBUG_ERROR,\r
338 "%a: unsupported alignment 0x%x\n",\r
339 __FUNCTION__,\r
340 Allocate->Alignment\r
341 ));\r
14b0faad
JJ
342 return EFI_UNSUPPORTED;\r
343 }\r
344\r
345 Status = QemuFwCfgFindFile ((CHAR8 *)Allocate->File, &FwCfgItem, &FwCfgSize);\r
346 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
347 DEBUG ((\r
348 DEBUG_ERROR,\r
349 "%a: QemuFwCfgFindFile(\"%a\"): %r\n",\r
350 __FUNCTION__,\r
351 Allocate->File,\r
352 Status\r
353 ));\r
14b0faad
JJ
354 return Status;\r
355 }\r
356\r
357 NumPages = EFI_SIZE_TO_PAGES (FwCfgSize);\r
ac0a286f 358 Address = MAX_UINT64;\r
4275f385
LE
359 if (OrderedCollectionFind (\r
360 AllocationsRestrictedTo32Bit,\r
361 Allocate->File\r
ac0a286f
MK
362 ) != NULL)\r
363 {\r
4275f385
LE
364 Address = MAX_UINT32;\r
365 }\r
ac0a286f
MK
366\r
367 Status = gBS->AllocatePages (\r
368 AllocateMaxAddress,\r
369 EfiACPIMemoryNVS,\r
370 NumPages,\r
371 &Address\r
372 );\r
14b0faad
JJ
373 if (EFI_ERROR (Status)) {\r
374 return Status;\r
375 }\r
376\r
377 Blob = AllocatePool (sizeof *Blob);\r
378 if (Blob == NULL) {\r
379 Status = EFI_OUT_OF_RESOURCES;\r
380 goto FreePages;\r
381 }\r
ac0a286f 382\r
14b0faad 383 CopyMem (Blob->File, Allocate->File, QEMU_LOADER_FNAME_SIZE);\r
ac0a286f
MK
384 Blob->Size = FwCfgSize;\r
385 Blob->Base = (VOID *)(UINTN)Address;\r
14b0faad
JJ
386 Blob->HostsOnlyTableData = TRUE;\r
387\r
388 Status = OrderedCollectionInsert (Tracker, NULL, Blob);\r
389 if (Status == RETURN_ALREADY_STARTED) {\r
ac0a286f
MK
390 DEBUG ((\r
391 DEBUG_ERROR,\r
392 "%a: duplicated file \"%a\"\n",\r
393 __FUNCTION__,\r
394 Allocate->File\r
395 ));\r
14b0faad
JJ
396 Status = EFI_PROTOCOL_ERROR;\r
397 }\r
ac0a286f 398\r
14b0faad
JJ
399 if (EFI_ERROR (Status)) {\r
400 goto FreeBlob;\r
401 }\r
402\r
403 QemuFwCfgSelectItem (FwCfgItem);\r
404 QemuFwCfgReadBytes (FwCfgSize, Blob->Base);\r
405 ZeroMem (Blob->Base + Blob->Size, EFI_PAGES_TO_SIZE (NumPages) - Blob->Size);\r
406\r
ac0a286f
MK
407 DEBUG ((\r
408 DEBUG_VERBOSE,\r
409 "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "\r
410 "Address=0x%Lx\n",\r
411 __FUNCTION__,\r
412 Allocate->File,\r
413 Allocate->Alignment,\r
414 Allocate->Zone,\r
415 (UINT64)Blob->Size,\r
416 (UINT64)(UINTN)Blob->Base\r
417 ));\r
14b0faad
JJ
418 return EFI_SUCCESS;\r
419\r
420FreeBlob:\r
421 FreePool (Blob);\r
422\r
423FreePages:\r
424 gBS->FreePages (Address, NumPages);\r
425\r
426 return Status;\r
427}\r
428\r
14b0faad
JJ
429/**\r
430 Process a QEMU_LOADER_ADD_POINTER command.\r
431\r
432 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
433\r
434 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
435 structures created thus far.\r
436\r
437 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
438 AddPointer, or the AddPointer command references\r
439 a file unknown to Tracker, or the pointer to\r
440 relocate has invalid location, size, or value, or\r
441 the relocated pointer value is not representable\r
442 in the given pointer size.\r
443\r
444 @retval EFI_SUCCESS The pointer field inside the pointer blob has\r
445 been relocated.\r
446**/\r
447STATIC\r
448EFI_STATUS\r
449EFIAPI\r
450ProcessCmdAddPointer (\r
ac0a286f
MK
451 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
452 IN CONST ORDERED_COLLECTION *Tracker\r
14b0faad
JJ
453 )\r
454{\r
ac0a286f
MK
455 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
456 BLOB *Blob, *Blob2;\r
457 UINT8 *PointerField;\r
458 UINT64 PointerValue;\r
459\r
460 if ((AddPointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||\r
461 (AddPointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))\r
462 {\r
70d5086c 463 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
464 return EFI_PROTOCOL_ERROR;\r
465 }\r
466\r
ac0a286f 467 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
14b0faad 468 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
ac0a286f
MK
469 if ((TrackerEntry == NULL) || (TrackerEntry2 == NULL)) {\r
470 DEBUG ((\r
471 DEBUG_ERROR,\r
472 "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",\r
473 __FUNCTION__,\r
474 AddPointer->PointerFile,\r
475 AddPointer->PointeeFile\r
476 ));\r
14b0faad
JJ
477 return EFI_PROTOCOL_ERROR;\r
478 }\r
479\r
ac0a286f 480 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
14b0faad 481 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
ac0a286f
MK
482 if (((AddPointer->PointerSize != 1) && (AddPointer->PointerSize != 2) &&\r
483 (AddPointer->PointerSize != 4) && (AddPointer->PointerSize != 8)) ||\r
484 (Blob->Size < AddPointer->PointerSize) ||\r
485 (Blob->Size - AddPointer->PointerSize < AddPointer->PointerOffset))\r
486 {\r
487 DEBUG ((\r
488 DEBUG_ERROR,\r
489 "%a: invalid pointer location or size in \"%a\"\n",\r
490 __FUNCTION__,\r
491 AddPointer->PointerFile\r
492 ));\r
14b0faad
JJ
493 return EFI_PROTOCOL_ERROR;\r
494 }\r
495\r
496 PointerField = Blob->Base + AddPointer->PointerOffset;\r
497 PointerValue = 0;\r
498 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
499 if (PointerValue >= Blob2->Size) {\r
ac0a286f
MK
500 DEBUG ((\r
501 DEBUG_ERROR,\r
502 "%a: invalid pointer value in \"%a\"\n",\r
503 __FUNCTION__,\r
504 AddPointer->PointerFile\r
505 ));\r
14b0faad
JJ
506 return EFI_PROTOCOL_ERROR;\r
507 }\r
508\r
509 //\r
510 // The memory allocation system ensures that the address of the byte past the\r
511 // last byte of any allocated object is expressible (no wraparound).\r
512 //\r
513 ASSERT ((UINTN)Blob2->Base <= MAX_ADDRESS - Blob2->Size);\r
514\r
515 PointerValue += (UINT64)(UINTN)Blob2->Base;\r
ac0a286f
MK
516 if ((AddPointer->PointerSize < 8) &&\r
517 (RShiftU64 (PointerValue, AddPointer->PointerSize * 8) != 0))\r
518 {\r
519 DEBUG ((\r
520 DEBUG_ERROR,\r
521 "%a: relocated pointer value unrepresentable in "\r
522 "\"%a\"\n",\r
523 __FUNCTION__,\r
524 AddPointer->PointerFile\r
525 ));\r
14b0faad
JJ
526 return EFI_PROTOCOL_ERROR;\r
527 }\r
528\r
529 CopyMem (PointerField, &PointerValue, AddPointer->PointerSize);\r
530\r
ac0a286f
MK
531 DEBUG ((\r
532 DEBUG_VERBOSE,\r
533 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
534 "PointerOffset=0x%x PointerSize=%d\n",\r
535 __FUNCTION__,\r
536 AddPointer->PointerFile,\r
537 AddPointer->PointeeFile,\r
538 AddPointer->PointerOffset,\r
539 AddPointer->PointerSize\r
540 ));\r
14b0faad
JJ
541 return EFI_SUCCESS;\r
542}\r
543\r
14b0faad
JJ
544/**\r
545 Process a QEMU_LOADER_ADD_CHECKSUM command.\r
546\r
547 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.\r
548\r
549 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
550 structures created thus far.\r
551\r
552 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in\r
553 AddChecksum, or the AddChecksum command\r
554 references a file unknown to Tracker, or the\r
555 range to checksum is invalid.\r
556\r
557 @retval EFI_SUCCESS The requested range has been checksummed.\r
558**/\r
559STATIC\r
560EFI_STATUS\r
561EFIAPI\r
562ProcessCmdAddChecksum (\r
ac0a286f
MK
563 IN CONST QEMU_LOADER_ADD_CHECKSUM *AddChecksum,\r
564 IN CONST ORDERED_COLLECTION *Tracker\r
14b0faad
JJ
565 )\r
566{\r
ac0a286f
MK
567 ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
568 BLOB *Blob;\r
14b0faad
JJ
569\r
570 if (AddChecksum->File[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {\r
70d5086c 571 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
14b0faad
JJ
572 return EFI_PROTOCOL_ERROR;\r
573 }\r
574\r
575 TrackerEntry = OrderedCollectionFind (Tracker, AddChecksum->File);\r
576 if (TrackerEntry == NULL) {\r
ac0a286f
MK
577 DEBUG ((\r
578 DEBUG_ERROR,\r
579 "%a: invalid blob reference \"%a\"\n",\r
580 __FUNCTION__,\r
581 AddChecksum->File\r
582 ));\r
14b0faad
JJ
583 return EFI_PROTOCOL_ERROR;\r
584 }\r
585\r
586 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
ac0a286f
MK
587 if ((Blob->Size <= AddChecksum->ResultOffset) ||\r
588 (Blob->Size < AddChecksum->Length) ||\r
589 (Blob->Size - AddChecksum->Length < AddChecksum->Start))\r
590 {\r
591 DEBUG ((\r
592 DEBUG_ERROR,\r
593 "%a: invalid checksum range in \"%a\"\n",\r
594 __FUNCTION__,\r
595 AddChecksum->File\r
596 ));\r
14b0faad
JJ
597 return EFI_PROTOCOL_ERROR;\r
598 }\r
599\r
600 Blob->Base[AddChecksum->ResultOffset] = CalculateCheckSum8 (\r
ac0a286f
MK
601 Blob->Base + AddChecksum->Start,\r
602 AddChecksum->Length\r
603 );\r
604 DEBUG ((\r
605 DEBUG_VERBOSE,\r
606 "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "\r
607 "Length=0x%x\n",\r
608 __FUNCTION__,\r
609 AddChecksum->File,\r
610 AddChecksum->ResultOffset,\r
611 AddChecksum->Start,\r
612 AddChecksum->Length\r
613 ));\r
14b0faad
JJ
614 return EFI_SUCCESS;\r
615}\r
616\r
9965cbd4
LE
617/**\r
618 Process a QEMU_LOADER_WRITE_POINTER command.\r
619\r
620 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to process.\r
621\r
622 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
623 structures created thus far.\r
624\r
df73df13
LE
625 @param[in,out] S3Context The S3_CONTEXT object capturing the fw_cfg actions\r
626 of successfully processed QEMU_LOADER_WRITE_POINTER\r
627 commands, to be replayed at S3 resume. S3Context\r
628 may be NULL if S3 is disabled.\r
629\r
9965cbd4
LE
630 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in\r
631 WritePointer. Or, the WritePointer command\r
632 references a file unknown to Tracker or the\r
633 fw_cfg directory. Or, the pointer object to\r
634 rewrite has invalid location, size, or initial\r
635 relative value. Or, the pointer value to store\r
636 does not fit in the given pointer size.\r
637\r
638 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg\r
df73df13
LE
639 file has been written. If S3Context is not NULL,\r
640 then WritePointer has been condensed into\r
641 S3Context.\r
642\r
643 @return Error codes propagated from\r
644 SaveCondensedWritePointerToS3Context(). The\r
645 pointer object inside the writeable fw_cfg file\r
646 has not been written.\r
9965cbd4
LE
647**/\r
648STATIC\r
649EFI_STATUS\r
650ProcessCmdWritePointer (\r
ac0a286f
MK
651 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,\r
652 IN CONST ORDERED_COLLECTION *Tracker,\r
653 IN OUT S3_CONTEXT *S3Context OPTIONAL\r
9965cbd4
LE
654 )\r
655{\r
ac0a286f
MK
656 RETURN_STATUS Status;\r
657 FIRMWARE_CONFIG_ITEM PointerItem;\r
658 UINTN PointerItemSize;\r
659 ORDERED_COLLECTION_ENTRY *PointeeEntry;\r
660 BLOB *PointeeBlob;\r
661 UINT64 PointerValue;\r
662\r
663 if ((WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') ||\r
664 (WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0'))\r
665 {\r
9965cbd4
LE
666 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));\r
667 return EFI_PROTOCOL_ERROR;\r
668 }\r
669\r
ac0a286f
MK
670 Status = QemuFwCfgFindFile (\r
671 (CONST CHAR8 *)WritePointer->PointerFile,\r
672 &PointerItem,\r
673 &PointerItemSize\r
674 );\r
9965cbd4 675 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);\r
ac0a286f
MK
676 if (RETURN_ERROR (Status) || (PointeeEntry == NULL)) {\r
677 DEBUG ((\r
678 DEBUG_ERROR,\r
9965cbd4 679 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",\r
ac0a286f
MK
680 __FUNCTION__,\r
681 WritePointer->PointerFile,\r
682 WritePointer->PointeeFile\r
683 ));\r
9965cbd4
LE
684 return EFI_PROTOCOL_ERROR;\r
685 }\r
686\r
ac0a286f
MK
687 if (((WritePointer->PointerSize != 1) && (WritePointer->PointerSize != 2) &&\r
688 (WritePointer->PointerSize != 4) && (WritePointer->PointerSize != 8)) ||\r
9965cbd4
LE
689 (PointerItemSize < WritePointer->PointerSize) ||\r
690 (PointerItemSize - WritePointer->PointerSize <\r
ac0a286f
MK
691 WritePointer->PointerOffset))\r
692 {\r
693 DEBUG ((\r
694 DEBUG_ERROR,\r
695 "%a: invalid pointer location or size in \"%a\"\n",\r
696 __FUNCTION__,\r
697 WritePointer->PointerFile\r
698 ));\r
9965cbd4
LE
699 return EFI_PROTOCOL_ERROR;\r
700 }\r
701\r
ac0a286f 702 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);\r
9965cbd4
LE
703 PointerValue = WritePointer->PointeeOffset;\r
704 if (PointerValue >= PointeeBlob->Size) {\r
705 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));\r
706 return EFI_PROTOCOL_ERROR;\r
707 }\r
708\r
709 //\r
710 // The memory allocation system ensures that the address of the byte past the\r
711 // last byte of any allocated object is expressible (no wraparound).\r
712 //\r
713 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);\r
714\r
715 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;\r
ac0a286f
MK
716 if ((WritePointer->PointerSize < 8) &&\r
717 (RShiftU64 (PointerValue, WritePointer->PointerSize * 8) != 0))\r
718 {\r
719 DEBUG ((\r
720 DEBUG_ERROR,\r
721 "%a: pointer value unrepresentable in \"%a\"\n",\r
722 __FUNCTION__,\r
723 WritePointer->PointerFile\r
724 ));\r
9965cbd4
LE
725 return EFI_PROTOCOL_ERROR;\r
726 }\r
727\r
df73df13
LE
728 //\r
729 // If S3 is enabled, we have to capture the below fw_cfg actions in condensed\r
730 // form, to be replayed during S3 resume.\r
731 //\r
732 if (S3Context != NULL) {\r
ac0a286f 733 EFI_STATUS SaveStatus;\r
df73df13
LE
734\r
735 SaveStatus = SaveCondensedWritePointerToS3Context (\r
736 S3Context,\r
737 (UINT16)PointerItem,\r
738 WritePointer->PointerSize,\r
739 WritePointer->PointerOffset,\r
740 PointerValue\r
741 );\r
742 if (EFI_ERROR (SaveStatus)) {\r
743 return SaveStatus;\r
744 }\r
745 }\r
746\r
9965cbd4
LE
747 QemuFwCfgSelectItem (PointerItem);\r
748 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
749 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
750\r
751 //\r
752 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob\r
753 // as unreleasable, for the case when the whole linker/loader script is\r
754 // handled successfully.\r
755 //\r
756 PointeeBlob->HostsOnlyTableData = FALSE;\r
757\r
ac0a286f
MK
758 DEBUG ((\r
759 DEBUG_VERBOSE,\r
760 "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "\r
761 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n",\r
762 __FUNCTION__,\r
763 WritePointer->PointerFile,\r
764 WritePointer->PointeeFile,\r
765 WritePointer->PointerOffset,\r
766 WritePointer->PointeeOffset,\r
767 WritePointer->PointerSize\r
768 ));\r
9965cbd4
LE
769 return EFI_SUCCESS;\r
770}\r
771\r
9965cbd4
LE
772/**\r
773 Undo a QEMU_LOADER_WRITE_POINTER command.\r
774\r
775 This function revokes (zeroes out) a guest memory reference communicated to\r
776 QEMU earlier. The caller is responsible for invoking this function only on\r
777 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed\r
778 by ProcessCmdWritePointer().\r
779\r
780 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.\r
781**/\r
782STATIC\r
783VOID\r
784UndoCmdWritePointer (\r
ac0a286f 785 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer\r
9965cbd4
LE
786 )\r
787{\r
ac0a286f
MK
788 RETURN_STATUS Status;\r
789 FIRMWARE_CONFIG_ITEM PointerItem;\r
790 UINTN PointerItemSize;\r
791 UINT64 PointerValue;\r
792\r
793 Status = QemuFwCfgFindFile (\r
794 (CONST CHAR8 *)WritePointer->PointerFile,\r
795 &PointerItem,\r
796 &PointerItemSize\r
797 );\r
9965cbd4
LE
798 ASSERT_RETURN_ERROR (Status);\r
799\r
800 PointerValue = 0;\r
801 QemuFwCfgSelectItem (PointerItem);\r
802 QemuFwCfgSkipBytes (WritePointer->PointerOffset);\r
803 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);\r
804\r
ac0a286f
MK
805 DEBUG ((\r
806 DEBUG_VERBOSE,\r
807 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n",\r
808 __FUNCTION__,\r
809 WritePointer->PointerFile,\r
810 WritePointer->PointerOffset,\r
811 WritePointer->PointerSize\r
812 ));\r
9965cbd4
LE
813}\r
814\r
14b0faad
JJ
815//\r
816// We'll be saving the keys of installed tables so that we can roll them back\r
817// in case of failure. 128 tables should be enough for anyone (TM).\r
818//\r
ac0a286f 819#define INSTALLED_TABLES_MAX 128\r
14b0faad
JJ
820\r
821/**\r
822 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte\r
823 array is an ACPI table, and if so, install it.\r
824\r
825 This function assumes that the entire QEMU linker/loader command file has\r
8c0b0b34 826 been processed successfully in a prior first pass.\r
14b0faad
JJ
827\r
828 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.\r
829\r
830 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user\r
831 structures.\r
832\r
833 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
834\r
835 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN\r
836 elements, allocated by the caller. On output,\r
837 the function will have stored (appended) the\r
838 AcpiProtocol-internal key of the ACPI table that\r
839 the function has installed, if the AddPointer\r
840 command identified an ACPI table that is\r
841 different from RSDT and XSDT.\r
842\r
843 @param[in,out] NumInstalled On input, the number of entries already used in\r
844 InstalledKey; it must be in [0,\r
845 INSTALLED_TABLES_MAX] inclusive. On output, the\r
846 parameter is incremented if the AddPointer\r
847 command identified an ACPI table that is\r
848 different from RSDT and XSDT.\r
849\r
072060a6
PDJ
850 @param[in,out] SeenPointers The ORDERED_COLLECTION tracking the absolute\r
851 target addresses that have been pointed-to by\r
852 QEMU_LOADER_ADD_POINTER commands thus far. If a\r
853 target address is encountered for the first\r
854 time, and it identifies an ACPI table that is\r
855 different from RDST and XSDT, the table is\r
856 installed. If a target address is seen for the\r
857 second or later times, it is skipped without\r
858 taking any action.\r
859\r
14b0faad
JJ
860 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on\r
861 input.\r
862\r
863 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI\r
864 table different from RSDT and XSDT, but there\r
865 was no more room in InstalledKey.\r
866\r
072060a6
PDJ
867 @retval EFI_SUCCESS AddPointer has been processed. Either its\r
868 absolute target address has been encountered\r
869 before, or an ACPI table different from RSDT\r
870 and XSDT has been installed (reflected by\r
871 InstalledKey and NumInstalled), or RSDT or\r
872 XSDT has been identified but not installed, or\r
873 the fw_cfg blob pointed-into by AddPointer has\r
874 been marked as hosting something else than\r
875 just direct ACPI table contents.\r
14b0faad
JJ
876\r
877 @return Error codes returned by\r
878 AcpiProtocol->InstallAcpiTable().\r
879**/\r
880STATIC\r
881EFI_STATUS\r
882EFIAPI\r
883Process2ndPassCmdAddPointer (\r
ac0a286f
MK
884 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,\r
885 IN CONST ORDERED_COLLECTION *Tracker,\r
886 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,\r
887 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],\r
888 IN OUT INT32 *NumInstalled,\r
889 IN OUT ORDERED_COLLECTION *SeenPointers\r
14b0faad
JJ
890 )\r
891{\r
ac0a286f
MK
892 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;\r
893 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;\r
894 ORDERED_COLLECTION_ENTRY *SeenPointerEntry;\r
895 CONST BLOB *Blob;\r
896 BLOB *Blob2;\r
897 CONST UINT8 *PointerField;\r
898 UINT64 PointerValue;\r
899 UINTN Blob2Remaining;\r
900 UINTN TableSize;\r
901 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;\r
902 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;\r
903 EFI_STATUS Status;\r
904\r
905 if ((*NumInstalled < 0) || (*NumInstalled > INSTALLED_TABLES_MAX)) {\r
14b0faad
JJ
906 return EFI_INVALID_PARAMETER;\r
907 }\r
908\r
ac0a286f 909 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);\r
14b0faad 910 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);\r
ac0a286f
MK
911 Blob = OrderedCollectionUserStruct (TrackerEntry);\r
912 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);\r
913 PointerField = Blob->Base + AddPointer->PointerOffset;\r
914 PointerValue = 0;\r
14b0faad
JJ
915 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);\r
916\r
917 //\r
918 // We assert that PointerValue falls inside Blob2's contents. This is ensured\r
919 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().\r
920 //\r
921 Blob2Remaining = (UINTN)Blob2->Base;\r
ac0a286f 922 ASSERT (PointerValue >= Blob2Remaining);\r
14b0faad
JJ
923 Blob2Remaining += Blob2->Size;\r
924 ASSERT (PointerValue < Blob2Remaining);\r
925\r
072060a6
PDJ
926 Status = OrderedCollectionInsert (\r
927 SeenPointers,\r
928 &SeenPointerEntry, // for reverting insertion in error case\r
929 (VOID *)(UINTN)PointerValue\r
930 );\r
931 if (EFI_ERROR (Status)) {\r
932 if (Status == RETURN_ALREADY_STARTED) {\r
933 //\r
934 // Already seen this pointer, don't try to process it again.\r
935 //\r
936 DEBUG ((\r
937 DEBUG_VERBOSE,\r
938 "%a: PointerValue=0x%Lx already processed, skipping.\n",\r
939 __FUNCTION__,\r
940 PointerValue\r
941 ));\r
942 Status = EFI_SUCCESS;\r
943 }\r
ac0a286f 944\r
072060a6
PDJ
945 return Status;\r
946 }\r
947\r
ac0a286f
MK
948 Blob2Remaining -= (UINTN)PointerValue;\r
949 DEBUG ((\r
950 DEBUG_VERBOSE,\r
951 "%a: checking for ACPI header in \"%a\" at 0x%Lx "\r
952 "(remaining: 0x%Lx): ",\r
953 __FUNCTION__,\r
954 AddPointer->PointeeFile,\r
955 PointerValue,\r
956 (UINT64)Blob2Remaining\r
957 ));\r
14b0faad
JJ
958\r
959 TableSize = 0;\r
960\r
961 //\r
962 // To make our job simple, the FACS has a custom header. Sigh.\r
963 //\r
964 if (sizeof *Facs <= Blob2Remaining) {\r
965 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;\r
966\r
ac0a286f
MK
967 if ((Facs->Length >= sizeof *Facs) &&\r
968 (Facs->Length <= Blob2Remaining) &&\r
969 (Facs->Signature ==\r
970 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE))\r
971 {\r
972 DEBUG ((\r
973 DEBUG_VERBOSE,\r
974 "found \"%-4.4a\" size 0x%x\n",\r
975 (CONST CHAR8 *)&Facs->Signature,\r
976 Facs->Length\r
977 ));\r
14b0faad
JJ
978 TableSize = Facs->Length;\r
979 }\r
980 }\r
981\r
982 //\r
983 // check for the uniform tables\r
984 //\r
ac0a286f 985 if ((TableSize == 0) && (sizeof *Header <= Blob2Remaining)) {\r
14b0faad
JJ
986 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;\r
987\r
ac0a286f
MK
988 if ((Header->Length >= sizeof *Header) &&\r
989 (Header->Length <= Blob2Remaining) &&\r
990 (CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0))\r
991 {\r
14b0faad
JJ
992 //\r
993 // This looks very much like an ACPI table from QEMU:\r
994 // - Length field consistent with both ACPI and containing blob size\r
995 // - checksum is correct\r
996 //\r
ac0a286f
MK
997 DEBUG ((\r
998 DEBUG_VERBOSE,\r
999 "found \"%-4.4a\" size 0x%x\n",\r
1000 (CONST CHAR8 *)&Header->Signature,\r
1001 Header->Length\r
1002 ));\r
14b0faad
JJ
1003 TableSize = Header->Length;\r
1004\r
1005 //\r
1006 // Skip RSDT and XSDT because those are handled by\r
1007 // EFI_ACPI_TABLE_PROTOCOL automatically.\r
ac0a286f
MK
1008 if ((Header->Signature ==\r
1009 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) ||\r
1010 (Header->Signature ==\r
1011 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))\r
1012 {\r
14b0faad
JJ
1013 return EFI_SUCCESS;\r
1014 }\r
1015 }\r
1016 }\r
1017\r
1018 if (TableSize == 0) {\r
70d5086c 1019 DEBUG ((DEBUG_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));\r
14b0faad
JJ
1020 Blob2->HostsOnlyTableData = FALSE;\r
1021 return EFI_SUCCESS;\r
1022 }\r
1023\r
1024 if (*NumInstalled == INSTALLED_TABLES_MAX) {\r
ac0a286f
MK
1025 DEBUG ((\r
1026 DEBUG_ERROR,\r
1027 "%a: can't install more than %d tables\n",\r
1028 __FUNCTION__,\r
1029 INSTALLED_TABLES_MAX\r
1030 ));\r
072060a6
PDJ
1031 Status = EFI_OUT_OF_RESOURCES;\r
1032 goto RollbackSeenPointer;\r
14b0faad
JJ
1033 }\r
1034\r
ac0a286f
MK
1035 Status = AcpiProtocol->InstallAcpiTable (\r
1036 AcpiProtocol,\r
1037 (VOID *)(UINTN)PointerValue,\r
1038 TableSize,\r
1039 &InstalledKey[*NumInstalled]\r
1040 );\r
14b0faad 1041 if (EFI_ERROR (Status)) {\r
ac0a286f
MK
1042 DEBUG ((\r
1043 DEBUG_ERROR,\r
1044 "%a: InstallAcpiTable(): %r\n",\r
1045 __FUNCTION__,\r
1046 Status\r
1047 ));\r
072060a6 1048 goto RollbackSeenPointer;\r
14b0faad 1049 }\r
ac0a286f 1050\r
14b0faad
JJ
1051 ++*NumInstalled;\r
1052 return EFI_SUCCESS;\r
072060a6
PDJ
1053\r
1054RollbackSeenPointer:\r
1055 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
1056 return Status;\r
14b0faad
JJ
1057}\r
1058\r
14b0faad
JJ
1059/**\r
1060 Download, process, and install ACPI table data from the QEMU loader\r
1061 interface.\r
1062\r
1063 @param[in] AcpiProtocol The ACPI table protocol used to install tables.\r
1064\r
1065 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU\r
1066 loader command with unsupported parameters\r
1067 has been found.\r
1068\r
1069 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg\r
1070 files.\r
1071\r
1072 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than\r
1073 INSTALLED_TABLES_MAX tables found.\r
1074\r
1075 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.\r
1076\r
1077 @return Status codes returned by\r
1078 AcpiProtocol->InstallAcpiTable().\r
1079\r
1080**/\r
1081EFI_STATUS\r
1082EFIAPI\r
f186536b 1083InstallQemuFwCfgTables (\r
ac0a286f 1084 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol\r
14b0faad
JJ
1085 )\r
1086{\r
ac0a286f
MK
1087 EFI_STATUS Status;\r
1088 FIRMWARE_CONFIG_ITEM FwCfgItem;\r
1089 UINTN FwCfgSize;\r
1090 QEMU_LOADER_ENTRY *LoaderStart;\r
1091 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;\r
1092 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;\r
1093 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;\r
1094 UINTN OriginalPciAttributesCount;\r
1095 ORDERED_COLLECTION *AllocationsRestrictedTo32Bit;\r
1096 S3_CONTEXT *S3Context;\r
1097 ORDERED_COLLECTION *Tracker;\r
1098 UINTN *InstalledKey;\r
1099 INT32 Installed;\r
1100 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;\r
1101 ORDERED_COLLECTION *SeenPointers;\r
1102 ORDERED_COLLECTION_ENTRY *SeenPointerEntry, *SeenPointerEntry2;\r
14b0faad
JJ
1103\r
1104 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);\r
1105 if (EFI_ERROR (Status)) {\r
1106 return Status;\r
1107 }\r
ac0a286f 1108\r
14b0faad 1109 if (FwCfgSize % sizeof *LoaderEntry != 0) {\r
ac0a286f
MK
1110 DEBUG ((\r
1111 DEBUG_ERROR,\r
1112 "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",\r
1113 __FUNCTION__,\r
1114 (UINT64)FwCfgSize\r
1115 ));\r
14b0faad
JJ
1116 return EFI_PROTOCOL_ERROR;\r
1117 }\r
1118\r
1119 LoaderStart = AllocatePool (FwCfgSize);\r
1120 if (LoaderStart == NULL) {\r
1121 return EFI_OUT_OF_RESOURCES;\r
1122 }\r
ac0a286f 1123\r
8f35eb92 1124 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);\r
14b0faad
JJ
1125 QemuFwCfgSelectItem (FwCfgItem);\r
1126 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);\r
8f35eb92 1127 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);\r
14b0faad
JJ
1128 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;\r
1129\r
8f98c76f 1130 AllocationsRestrictedTo32Bit = NULL;\r
ac0a286f
MK
1131 Status = CollectAllocationsRestrictedTo32Bit (\r
1132 &AllocationsRestrictedTo32Bit,\r
1133 LoaderStart,\r
1134 LoaderEnd\r
1135 );\r
4275f385
LE
1136 if (EFI_ERROR (Status)) {\r
1137 goto FreeLoader;\r
1138 }\r
1139\r
df73df13
LE
1140 S3Context = NULL;\r
1141 if (QemuFwCfgS3Enabled ()) {\r
1142 //\r
1143 // Size the allocation pessimistically, assuming that all commands in the\r
1144 // script are QEMU_LOADER_WRITE_POINTER commands.\r
1145 //\r
1146 Status = AllocateS3Context (&S3Context, LoaderEnd - LoaderStart);\r
1147 if (EFI_ERROR (Status)) {\r
4275f385 1148 goto FreeAllocationsRestrictedTo32Bit;\r
df73df13
LE
1149 }\r
1150 }\r
1151\r
14b0faad
JJ
1152 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);\r
1153 if (Tracker == NULL) {\r
1154 Status = EFI_OUT_OF_RESOURCES;\r
df73df13 1155 goto FreeS3Context;\r
14b0faad
JJ
1156 }\r
1157\r
1158 //\r
1159 // first pass: process the commands\r
1160 //\r
9965cbd4
LE
1161 // "WritePointerSubsetEnd" points one past the last successful\r
1162 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first\r
1163 // pass, no such command has been encountered yet.\r
1164 //\r
1165 WritePointerSubsetEnd = LoaderStart;\r
14b0faad
JJ
1166 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1167 switch (LoaderEntry->Type) {\r
ac0a286f
MK
1168 case QemuLoaderCmdAllocate:\r
1169 Status = ProcessCmdAllocate (\r
1170 &LoaderEntry->Command.Allocate,\r
1171 Tracker,\r
1172 AllocationsRestrictedTo32Bit\r
1173 );\r
1174 break;\r
14b0faad 1175\r
ac0a286f
MK
1176 case QemuLoaderCmdAddPointer:\r
1177 Status = ProcessCmdAddPointer (\r
1178 &LoaderEntry->Command.AddPointer,\r
1179 Tracker\r
1180 );\r
1181 break;\r
14b0faad 1182\r
ac0a286f
MK
1183 case QemuLoaderCmdAddChecksum:\r
1184 Status = ProcessCmdAddChecksum (\r
1185 &LoaderEntry->Command.AddChecksum,\r
1186 Tracker\r
1187 );\r
1188 break;\r
14b0faad 1189\r
ac0a286f
MK
1190 case QemuLoaderCmdWritePointer:\r
1191 Status = ProcessCmdWritePointer (\r
1192 &LoaderEntry->Command.WritePointer,\r
1193 Tracker,\r
1194 S3Context\r
1195 );\r
9965cbd4
LE
1196 if (!EFI_ERROR (Status)) {\r
1197 WritePointerSubsetEnd = LoaderEntry + 1;\r
1198 }\r
ac0a286f 1199\r
9965cbd4
LE
1200 break;\r
1201\r
ac0a286f
MK
1202 default:\r
1203 DEBUG ((\r
1204 DEBUG_VERBOSE,\r
1205 "%a: unknown loader command: 0x%x\n",\r
1206 __FUNCTION__,\r
1207 LoaderEntry->Type\r
1208 ));\r
1209 break;\r
14b0faad
JJ
1210 }\r
1211\r
1212 if (EFI_ERROR (Status)) {\r
9965cbd4 1213 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
1214 }\r
1215 }\r
1216\r
1217 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);\r
1218 if (InstalledKey == NULL) {\r
1219 Status = EFI_OUT_OF_RESOURCES;\r
9965cbd4 1220 goto RollbackWritePointersAndFreeTracker;\r
14b0faad
JJ
1221 }\r
1222\r
072060a6
PDJ
1223 SeenPointers = OrderedCollectionInit (PointerCompare, PointerCompare);\r
1224 if (SeenPointers == NULL) {\r
1225 Status = EFI_OUT_OF_RESOURCES;\r
1226 goto FreeKeys;\r
1227 }\r
1228\r
14b0faad
JJ
1229 //\r
1230 // second pass: identify and install ACPI tables\r
1231 //\r
1232 Installed = 0;\r
1233 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {\r
1234 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {\r
072060a6
PDJ
1235 Status = Process2ndPassCmdAddPointer (\r
1236 &LoaderEntry->Command.AddPointer,\r
1237 Tracker,\r
1238 AcpiProtocol,\r
1239 InstalledKey,\r
1240 &Installed,\r
1241 SeenPointers\r
1242 );\r
14b0faad 1243 if (EFI_ERROR (Status)) {\r
df73df13 1244 goto UninstallAcpiTables;\r
14b0faad
JJ
1245 }\r
1246 }\r
1247 }\r
1248\r
df73df13
LE
1249 //\r
1250 // Translating the condensed QEMU_LOADER_WRITE_POINTER commands to ACPI S3\r
1251 // Boot Script opcodes has to be the last operation in this function, because\r
1252 // if it succeeds, it cannot be undone.\r
1253 //\r
1254 if (S3Context != NULL) {\r
1255 Status = TransferS3ContextToBootScript (S3Context);\r
80576225
LE
1256 if (EFI_ERROR (Status)) {\r
1257 goto UninstallAcpiTables;\r
1258 }\r
ac0a286f 1259\r
80576225 1260 //\r
a2e75595 1261 // Ownership of S3Context has been transferred.\r
80576225
LE
1262 //\r
1263 S3Context = NULL;\r
df73df13
LE
1264 }\r
1265\r
1266UninstallAcpiTables:\r
14b0faad
JJ
1267 if (EFI_ERROR (Status)) {\r
1268 //\r
1269 // roll back partial installation\r
1270 //\r
1271 while (Installed > 0) {\r
1272 --Installed;\r
1273 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);\r
1274 }\r
1275 } else {\r
70d5086c 1276 DEBUG ((DEBUG_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));\r
14b0faad
JJ
1277 }\r
1278\r
072060a6
PDJ
1279 for (SeenPointerEntry = OrderedCollectionMin (SeenPointers);\r
1280 SeenPointerEntry != NULL;\r
ac0a286f
MK
1281 SeenPointerEntry = SeenPointerEntry2)\r
1282 {\r
072060a6
PDJ
1283 SeenPointerEntry2 = OrderedCollectionNext (SeenPointerEntry);\r
1284 OrderedCollectionDelete (SeenPointers, SeenPointerEntry, NULL);\r
1285 }\r
ac0a286f 1286\r
072060a6
PDJ
1287 OrderedCollectionUninit (SeenPointers);\r
1288\r
1289FreeKeys:\r
14b0faad
JJ
1290 FreePool (InstalledKey);\r
1291\r
9965cbd4
LE
1292RollbackWritePointersAndFreeTracker:\r
1293 //\r
1294 // In case of failure, revoke any allocation addresses that were communicated\r
1295 // to QEMU previously, before we release all the blobs.\r
1296 //\r
1297 if (EFI_ERROR (Status)) {\r
1298 LoaderEntry = WritePointerSubsetEnd;\r
1299 while (LoaderEntry > LoaderStart) {\r
1300 --LoaderEntry;\r
1301 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {\r
1302 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);\r
1303 }\r
1304 }\r
1305 }\r
1306\r
14b0faad
JJ
1307 //\r
1308 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in\r
1309 // place only if we're exiting with success and the blob hosts data that is\r
1310 // not directly part of some ACPI table.\r
1311 //\r
1312 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;\r
ac0a286f
MK
1313 TrackerEntry = TrackerEntry2)\r
1314 {\r
1315 VOID *UserStruct;\r
1316 BLOB *Blob;\r
14b0faad
JJ
1317\r
1318 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);\r
1319 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);\r
1320 Blob = UserStruct;\r
1321\r
1322 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {\r
ac0a286f
MK
1323 DEBUG ((\r
1324 DEBUG_VERBOSE,\r
1325 "%a: freeing \"%a\"\n",\r
1326 __FUNCTION__,\r
1327 Blob->File\r
1328 ));\r
14b0faad
JJ
1329 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));\r
1330 }\r
ac0a286f 1331\r
14b0faad
JJ
1332 FreePool (Blob);\r
1333 }\r
ac0a286f 1334\r
14b0faad
JJ
1335 OrderedCollectionUninit (Tracker);\r
1336\r
df73df13
LE
1337FreeS3Context:\r
1338 if (S3Context != NULL) {\r
1339 ReleaseS3Context (S3Context);\r
1340 }\r
1341\r
4275f385
LE
1342FreeAllocationsRestrictedTo32Bit:\r
1343 ReleaseAllocationsRestrictedTo32Bit (AllocationsRestrictedTo32Bit);\r
1344\r
14b0faad
JJ
1345FreeLoader:\r
1346 FreePool (LoaderStart);\r
1347\r
1348 return Status;\r
1349}\r