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