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