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