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