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