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