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