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