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