]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/AcpiPlatformDxe/QemuFwCfgAcpi.c
OvmfPkg/AcpiPlatformDxe: implement the QEMU_LOADER_WRITE_POINTER command
[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 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
364 WritePointer. Or, the WritePointer command
365 references a file unknown to Tracker or the
366 fw_cfg directory. Or, the pointer object to
367 rewrite has invalid location, size, or initial
368 relative value. Or, the pointer value to store
369 does not fit in the given pointer size.
370
371 @retval EFI_SUCCESS The pointer object inside the writeable fw_cfg
372 file has been written.
373 **/
374 STATIC
375 EFI_STATUS
376 ProcessCmdWritePointer (
377 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer,
378 IN CONST ORDERED_COLLECTION *Tracker
379 )
380 {
381 RETURN_STATUS Status;
382 FIRMWARE_CONFIG_ITEM PointerItem;
383 UINTN PointerItemSize;
384 ORDERED_COLLECTION_ENTRY *PointeeEntry;
385 BLOB *PointeeBlob;
386 UINT64 PointerValue;
387
388 if (WritePointer->PointerFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0' ||
389 WritePointer->PointeeFile[QEMU_LOADER_FNAME_SIZE - 1] != '\0') {
390 DEBUG ((DEBUG_ERROR, "%a: malformed file name\n", __FUNCTION__));
391 return EFI_PROTOCOL_ERROR;
392 }
393
394 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
395 &PointerItem, &PointerItemSize);
396 PointeeEntry = OrderedCollectionFind (Tracker, WritePointer->PointeeFile);
397 if (RETURN_ERROR (Status) || PointeeEntry == NULL) {
398 DEBUG ((DEBUG_ERROR,
399 "%a: invalid fw_cfg file or blob reference \"%a\" / \"%a\"\n",
400 __FUNCTION__, WritePointer->PointerFile, WritePointer->PointeeFile));
401 return EFI_PROTOCOL_ERROR;
402 }
403
404 if ((WritePointer->PointerSize != 1 && WritePointer->PointerSize != 2 &&
405 WritePointer->PointerSize != 4 && WritePointer->PointerSize != 8) ||
406 (PointerItemSize < WritePointer->PointerSize) ||
407 (PointerItemSize - WritePointer->PointerSize <
408 WritePointer->PointerOffset)) {
409 DEBUG ((DEBUG_ERROR, "%a: invalid pointer location or size in \"%a\"\n",
410 __FUNCTION__, WritePointer->PointerFile));
411 return EFI_PROTOCOL_ERROR;
412 }
413
414 PointeeBlob = OrderedCollectionUserStruct (PointeeEntry);
415 PointerValue = WritePointer->PointeeOffset;
416 if (PointerValue >= PointeeBlob->Size) {
417 DEBUG ((DEBUG_ERROR, "%a: invalid PointeeOffset\n", __FUNCTION__));
418 return EFI_PROTOCOL_ERROR;
419 }
420
421 //
422 // The memory allocation system ensures that the address of the byte past the
423 // last byte of any allocated object is expressible (no wraparound).
424 //
425 ASSERT ((UINTN)PointeeBlob->Base <= MAX_ADDRESS - PointeeBlob->Size);
426
427 PointerValue += (UINT64)(UINTN)PointeeBlob->Base;
428 if (RShiftU64 (
429 RShiftU64 (PointerValue, WritePointer->PointerSize * 8 - 1), 1) != 0) {
430 DEBUG ((DEBUG_ERROR, "%a: pointer value unrepresentable in \"%a\"\n",
431 __FUNCTION__, WritePointer->PointerFile));
432 return EFI_PROTOCOL_ERROR;
433 }
434
435 QemuFwCfgSelectItem (PointerItem);
436 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
437 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
438
439 //
440 // Because QEMU has now learned PointeeBlob->Base, we must mark PointeeBlob
441 // as unreleasable, for the case when the whole linker/loader script is
442 // handled successfully.
443 //
444 PointeeBlob->HostsOnlyTableData = FALSE;
445
446 DEBUG ((DEBUG_VERBOSE, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
447 "PointerOffset=0x%x PointeeOffset=0x%x PointerSize=%d\n", __FUNCTION__,
448 WritePointer->PointerFile, WritePointer->PointeeFile,
449 WritePointer->PointerOffset, WritePointer->PointeeOffset,
450 WritePointer->PointerSize));
451 return EFI_SUCCESS;
452 }
453
454
455 /**
456 Undo a QEMU_LOADER_WRITE_POINTER command.
457
458 This function revokes (zeroes out) a guest memory reference communicated to
459 QEMU earlier. The caller is responsible for invoking this function only on
460 such QEMU_LOADER_WRITE_POINTER commands that have been successfully processed
461 by ProcessCmdWritePointer().
462
463 @param[in] WritePointer The QEMU_LOADER_WRITE_POINTER command to undo.
464 **/
465 STATIC
466 VOID
467 UndoCmdWritePointer (
468 IN CONST QEMU_LOADER_WRITE_POINTER *WritePointer
469 )
470 {
471 RETURN_STATUS Status;
472 FIRMWARE_CONFIG_ITEM PointerItem;
473 UINTN PointerItemSize;
474 UINT64 PointerValue;
475
476 Status = QemuFwCfgFindFile ((CONST CHAR8 *)WritePointer->PointerFile,
477 &PointerItem, &PointerItemSize);
478 ASSERT_RETURN_ERROR (Status);
479
480 PointerValue = 0;
481 QemuFwCfgSelectItem (PointerItem);
482 QemuFwCfgSkipBytes (WritePointer->PointerOffset);
483 QemuFwCfgWriteBytes (WritePointer->PointerSize, &PointerValue);
484
485 DEBUG ((DEBUG_VERBOSE,
486 "%a: PointerFile=\"%a\" PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__,
487 WritePointer->PointerFile, WritePointer->PointerOffset,
488 WritePointer->PointerSize));
489 }
490
491
492 //
493 // We'll be saving the keys of installed tables so that we can roll them back
494 // in case of failure. 128 tables should be enough for anyone (TM).
495 //
496 #define INSTALLED_TABLES_MAX 128
497
498 /**
499 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
500 array is an ACPI table, and if so, install it.
501
502 This function assumes that the entire QEMU linker/loader command file has
503 been processed successfully in a prior first pass.
504
505 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
506
507 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
508 structures.
509
510 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
511
512 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
513 elements, allocated by the caller. On output,
514 the function will have stored (appended) the
515 AcpiProtocol-internal key of the ACPI table that
516 the function has installed, if the AddPointer
517 command identified an ACPI table that is
518 different from RSDT and XSDT.
519
520 @param[in,out] NumInstalled On input, the number of entries already used in
521 InstalledKey; it must be in [0,
522 INSTALLED_TABLES_MAX] inclusive. On output, the
523 parameter is incremented if the AddPointer
524 command identified an ACPI table that is
525 different from RSDT and XSDT.
526
527 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
528 input.
529
530 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
531 table different from RSDT and XSDT, but there
532 was no more room in InstalledKey.
533
534 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
535 table different from RSDT and XSDT has been
536 installed (reflected by InstalledKey and
537 NumInstalled), or RSDT or XSDT has been
538 identified but not installed, or the fw_cfg
539 blob pointed-into by AddPointer has been
540 marked as hosting something else than just
541 direct ACPI table contents.
542
543 @return Error codes returned by
544 AcpiProtocol->InstallAcpiTable().
545 **/
546 STATIC
547 EFI_STATUS
548 EFIAPI
549 Process2ndPassCmdAddPointer (
550 IN CONST QEMU_LOADER_ADD_POINTER *AddPointer,
551 IN CONST ORDERED_COLLECTION *Tracker,
552 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol,
553 IN OUT UINTN InstalledKey[INSTALLED_TABLES_MAX],
554 IN OUT INT32 *NumInstalled
555 )
556 {
557 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry;
558 CONST ORDERED_COLLECTION_ENTRY *TrackerEntry2;
559 CONST BLOB *Blob;
560 BLOB *Blob2;
561 CONST UINT8 *PointerField;
562 UINT64 PointerValue;
563 UINTN Blob2Remaining;
564 UINTN TableSize;
565 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs;
566 CONST EFI_ACPI_DESCRIPTION_HEADER *Header;
567 EFI_STATUS Status;
568
569 if (*NumInstalled < 0 || *NumInstalled > INSTALLED_TABLES_MAX) {
570 return EFI_INVALID_PARAMETER;
571 }
572
573 TrackerEntry = OrderedCollectionFind (Tracker, AddPointer->PointerFile);
574 TrackerEntry2 = OrderedCollectionFind (Tracker, AddPointer->PointeeFile);
575 Blob = OrderedCollectionUserStruct (TrackerEntry);
576 Blob2 = OrderedCollectionUserStruct (TrackerEntry2);
577 PointerField = Blob->Base + AddPointer->PointerOffset;
578 PointerValue = 0;
579 CopyMem (&PointerValue, PointerField, AddPointer->PointerSize);
580
581 //
582 // We assert that PointerValue falls inside Blob2's contents. This is ensured
583 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
584 //
585 Blob2Remaining = (UINTN)Blob2->Base;
586 ASSERT(PointerValue >= Blob2Remaining);
587 Blob2Remaining += Blob2->Size;
588 ASSERT (PointerValue < Blob2Remaining);
589
590 Blob2Remaining -= (UINTN) PointerValue;
591 DEBUG ((EFI_D_VERBOSE, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
592 "(remaining: 0x%Lx): ", __FUNCTION__, AddPointer->PointeeFile,
593 PointerValue, (UINT64)Blob2Remaining));
594
595 TableSize = 0;
596
597 //
598 // To make our job simple, the FACS has a custom header. Sigh.
599 //
600 if (sizeof *Facs <= Blob2Remaining) {
601 Facs = (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)PointerValue;
602
603 if (Facs->Length >= sizeof *Facs &&
604 Facs->Length <= Blob2Remaining &&
605 Facs->Signature ==
606 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE) {
607 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
608 (CONST CHAR8 *)&Facs->Signature, Facs->Length));
609 TableSize = Facs->Length;
610 }
611 }
612
613 //
614 // check for the uniform tables
615 //
616 if (TableSize == 0 && sizeof *Header <= Blob2Remaining) {
617 Header = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN)PointerValue;
618
619 if (Header->Length >= sizeof *Header &&
620 Header->Length <= Blob2Remaining &&
621 CalculateSum8 ((CONST UINT8 *)Header, Header->Length) == 0) {
622 //
623 // This looks very much like an ACPI table from QEMU:
624 // - Length field consistent with both ACPI and containing blob size
625 // - checksum is correct
626 //
627 DEBUG ((EFI_D_VERBOSE, "found \"%-4.4a\" size 0x%x\n",
628 (CONST CHAR8 *)&Header->Signature, Header->Length));
629 TableSize = Header->Length;
630
631 //
632 // Skip RSDT and XSDT because those are handled by
633 // EFI_ACPI_TABLE_PROTOCOL automatically.
634 if (Header->Signature ==
635 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE ||
636 Header->Signature ==
637 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) {
638 return EFI_SUCCESS;
639 }
640 }
641 }
642
643 if (TableSize == 0) {
644 DEBUG ((EFI_D_VERBOSE, "not found; marking fw_cfg blob as opaque\n"));
645 Blob2->HostsOnlyTableData = FALSE;
646 return EFI_SUCCESS;
647 }
648
649 if (*NumInstalled == INSTALLED_TABLES_MAX) {
650 DEBUG ((EFI_D_ERROR, "%a: can't install more than %d tables\n",
651 __FUNCTION__, INSTALLED_TABLES_MAX));
652 return EFI_OUT_OF_RESOURCES;
653 }
654
655 Status = AcpiProtocol->InstallAcpiTable (AcpiProtocol,
656 (VOID *)(UINTN)PointerValue, TableSize,
657 &InstalledKey[*NumInstalled]);
658 if (EFI_ERROR (Status)) {
659 DEBUG ((EFI_D_ERROR, "%a: InstallAcpiTable(): %r\n", __FUNCTION__,
660 Status));
661 return Status;
662 }
663 ++*NumInstalled;
664 return EFI_SUCCESS;
665 }
666
667
668 /**
669 Download, process, and install ACPI table data from the QEMU loader
670 interface.
671
672 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
673
674 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
675 loader command with unsupported parameters
676 has been found.
677
678 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
679 files.
680
681 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
682 INSTALLED_TABLES_MAX tables found.
683
684 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
685
686 @return Status codes returned by
687 AcpiProtocol->InstallAcpiTable().
688
689 **/
690 EFI_STATUS
691 EFIAPI
692 InstallQemuFwCfgTables (
693 IN EFI_ACPI_TABLE_PROTOCOL *AcpiProtocol
694 )
695 {
696 EFI_STATUS Status;
697 FIRMWARE_CONFIG_ITEM FwCfgItem;
698 UINTN FwCfgSize;
699 QEMU_LOADER_ENTRY *LoaderStart;
700 CONST QEMU_LOADER_ENTRY *LoaderEntry, *LoaderEnd;
701 CONST QEMU_LOADER_ENTRY *WritePointerSubsetEnd;
702 ORIGINAL_ATTRIBUTES *OriginalPciAttributes;
703 UINTN OriginalPciAttributesCount;
704 ORDERED_COLLECTION *Tracker;
705 UINTN *InstalledKey;
706 INT32 Installed;
707 ORDERED_COLLECTION_ENTRY *TrackerEntry, *TrackerEntry2;
708
709 Status = QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem, &FwCfgSize);
710 if (EFI_ERROR (Status)) {
711 return Status;
712 }
713 if (FwCfgSize % sizeof *LoaderEntry != 0) {
714 DEBUG ((EFI_D_ERROR, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
715 __FUNCTION__, (UINT64)FwCfgSize));
716 return EFI_PROTOCOL_ERROR;
717 }
718
719 LoaderStart = AllocatePool (FwCfgSize);
720 if (LoaderStart == NULL) {
721 return EFI_OUT_OF_RESOURCES;
722 }
723 EnablePciDecoding (&OriginalPciAttributes, &OriginalPciAttributesCount);
724 QemuFwCfgSelectItem (FwCfgItem);
725 QemuFwCfgReadBytes (FwCfgSize, LoaderStart);
726 RestorePciDecoding (OriginalPciAttributes, OriginalPciAttributesCount);
727 LoaderEnd = LoaderStart + FwCfgSize / sizeof *LoaderEntry;
728
729 Tracker = OrderedCollectionInit (BlobCompare, BlobKeyCompare);
730 if (Tracker == NULL) {
731 Status = EFI_OUT_OF_RESOURCES;
732 goto FreeLoader;
733 }
734
735 //
736 // first pass: process the commands
737 //
738 // "WritePointerSubsetEnd" points one past the last successful
739 // QEMU_LOADER_WRITE_POINTER command. Now when we're about to start the first
740 // pass, no such command has been encountered yet.
741 //
742 WritePointerSubsetEnd = LoaderStart;
743 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
744 switch (LoaderEntry->Type) {
745 case QemuLoaderCmdAllocate:
746 Status = ProcessCmdAllocate (&LoaderEntry->Command.Allocate, Tracker);
747 break;
748
749 case QemuLoaderCmdAddPointer:
750 Status = ProcessCmdAddPointer (&LoaderEntry->Command.AddPointer,
751 Tracker);
752 break;
753
754 case QemuLoaderCmdAddChecksum:
755 Status = ProcessCmdAddChecksum (&LoaderEntry->Command.AddChecksum,
756 Tracker);
757 break;
758
759 case QemuLoaderCmdWritePointer:
760 Status = ProcessCmdWritePointer (&LoaderEntry->Command.WritePointer,
761 Tracker);
762 if (!EFI_ERROR (Status)) {
763 WritePointerSubsetEnd = LoaderEntry + 1;
764 }
765 break;
766
767 default:
768 DEBUG ((EFI_D_VERBOSE, "%a: unknown loader command: 0x%x\n",
769 __FUNCTION__, LoaderEntry->Type));
770 break;
771 }
772
773 if (EFI_ERROR (Status)) {
774 goto RollbackWritePointersAndFreeTracker;
775 }
776 }
777
778 InstalledKey = AllocatePool (INSTALLED_TABLES_MAX * sizeof *InstalledKey);
779 if (InstalledKey == NULL) {
780 Status = EFI_OUT_OF_RESOURCES;
781 goto RollbackWritePointersAndFreeTracker;
782 }
783
784 //
785 // second pass: identify and install ACPI tables
786 //
787 Installed = 0;
788 for (LoaderEntry = LoaderStart; LoaderEntry < LoaderEnd; ++LoaderEntry) {
789 if (LoaderEntry->Type == QemuLoaderCmdAddPointer) {
790 Status = Process2ndPassCmdAddPointer (&LoaderEntry->Command.AddPointer,
791 Tracker, AcpiProtocol, InstalledKey, &Installed);
792 if (EFI_ERROR (Status)) {
793 break;
794 }
795 }
796 }
797
798 if (EFI_ERROR (Status)) {
799 //
800 // roll back partial installation
801 //
802 while (Installed > 0) {
803 --Installed;
804 AcpiProtocol->UninstallAcpiTable (AcpiProtocol, InstalledKey[Installed]);
805 }
806 } else {
807 DEBUG ((EFI_D_INFO, "%a: installed %d tables\n", __FUNCTION__, Installed));
808 }
809
810 FreePool (InstalledKey);
811
812 RollbackWritePointersAndFreeTracker:
813 //
814 // In case of failure, revoke any allocation addresses that were communicated
815 // to QEMU previously, before we release all the blobs.
816 //
817 if (EFI_ERROR (Status)) {
818 LoaderEntry = WritePointerSubsetEnd;
819 while (LoaderEntry > LoaderStart) {
820 --LoaderEntry;
821 if (LoaderEntry->Type == QemuLoaderCmdWritePointer) {
822 UndoCmdWritePointer (&LoaderEntry->Command.WritePointer);
823 }
824 }
825 }
826
827 //
828 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
829 // place only if we're exiting with success and the blob hosts data that is
830 // not directly part of some ACPI table.
831 //
832 for (TrackerEntry = OrderedCollectionMin (Tracker); TrackerEntry != NULL;
833 TrackerEntry = TrackerEntry2) {
834 VOID *UserStruct;
835 BLOB *Blob;
836
837 TrackerEntry2 = OrderedCollectionNext (TrackerEntry);
838 OrderedCollectionDelete (Tracker, TrackerEntry, &UserStruct);
839 Blob = UserStruct;
840
841 if (EFI_ERROR (Status) || Blob->HostsOnlyTableData) {
842 DEBUG ((EFI_D_VERBOSE, "%a: freeing \"%a\"\n", __FUNCTION__,
843 Blob->File));
844 gBS->FreePages ((UINTN)Blob->Base, EFI_SIZE_TO_PAGES (Blob->Size));
845 }
846 FreePool (Blob);
847 }
848 OrderedCollectionUninit (Tracker);
849
850 FreeLoader:
851 FreePool (LoaderStart);
852
853 return Status;
854 }