4 Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR>
6 Copyright (C) 2012-2014, Red Hat, Inc.
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 #include "AcpiPlatform.h"
19 #include "QemuLoader.h"
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/QemuFwCfgLib.h>
23 #include <Library/DxeServicesTableLib.h>
24 #include <Library/PcdLib.h>
25 #include <Library/OrderedCollectionLib.h>
26 #include <IndustryStandard/Acpi.h>
33 if (!QemuFwCfgIsAvailable ()) {
48 // For all N >= 1, N bits are enough to represent the number of bits set
49 // among N bits. It's true for N == 1. When adding a new bit (N := N+1),
50 // the maximum number of possibly set bits increases by one, while the
51 // representable maximum doubles.
53 Mask
= ((Mask
& 0xAAAA) >> 1) + (Mask
& 0x5555);
54 Mask
= ((Mask
& 0xCCCC) >> 2) + (Mask
& 0x3333);
55 Mask
= ((Mask
& 0xF0F0) >> 4) + (Mask
& 0x0F0F);
56 Mask
= ((Mask
& 0xFF00) >> 8) + (Mask
& 0x00FF);
65 QemuInstallAcpiMadtTable (
66 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
67 IN VOID
*AcpiTableBuffer
,
68 IN UINTN AcpiTableBufferSize
,
73 UINTN PciLinkIsoCount
;
75 EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER
*Madt
;
76 EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC_STRUCTURE
*LocalApic
;
77 EFI_ACPI_1_0_IO_APIC_STRUCTURE
*IoApic
;
78 EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE
*Iso
;
79 EFI_ACPI_1_0_LOCAL_APIC_NMI_STRUCTURE
*LocalApicNmi
;
84 ASSERT (AcpiTableBufferSize
>= sizeof (EFI_ACPI_DESCRIPTION_HEADER
));
86 QemuFwCfgSelectItem (QemuFwCfgItemSmpCpuCount
);
87 CpuCount
= QemuFwCfgRead16 ();
88 ASSERT (CpuCount
>= 1);
91 // Set Level-tiggered, Active High for these identity mapped IRQs. The bitset
92 // corresponds to the union of all possible interrupt assignments for the LNKA,
93 // LNKB, LNKC, LNKD PCI interrupt lines. See the DSDT.
95 PciLinkIsoCount
= CountBits16 (PcdGet16 (Pcd8259LegacyModeEdgeLevel
));
97 NewBufferSize
= 1 * sizeof (*Madt
) +
98 CpuCount
* sizeof (*LocalApic
) +
99 1 * sizeof (*IoApic
) +
100 (1 + PciLinkIsoCount
) * sizeof (*Iso
) +
101 1 * sizeof (*LocalApicNmi
);
103 Madt
= AllocatePool (NewBufferSize
);
105 return EFI_OUT_OF_RESOURCES
;
108 CopyMem (&(Madt
->Header
), AcpiTableBuffer
, sizeof (EFI_ACPI_DESCRIPTION_HEADER
));
109 Madt
->Header
.Length
= (UINT32
) NewBufferSize
;
110 Madt
->LocalApicAddress
= PcdGet32 (PcdCpuLocalApicBaseAddress
);
111 Madt
->Flags
= EFI_ACPI_1_0_PCAT_COMPAT
;
115 for (Loop
= 0; Loop
< CpuCount
; ++Loop
) {
116 LocalApic
->Type
= EFI_ACPI_1_0_PROCESSOR_LOCAL_APIC
;
117 LocalApic
->Length
= sizeof (*LocalApic
);
118 LocalApic
->AcpiProcessorId
= (UINT8
) Loop
;
119 LocalApic
->ApicId
= (UINT8
) Loop
;
120 LocalApic
->Flags
= 1; // enabled
126 IoApic
->Type
= EFI_ACPI_1_0_IO_APIC
;
127 IoApic
->Length
= sizeof (*IoApic
);
128 IoApic
->IoApicId
= (UINT8
) CpuCount
;
129 IoApic
->Reserved
= EFI_ACPI_RESERVED_BYTE
;
130 IoApic
->IoApicAddress
= 0xFEC00000;
131 IoApic
->SystemVectorBase
= 0x00000000;
135 // IRQ0 (8254 Timer) => IRQ2 (PIC) Interrupt Source Override Structure
138 Iso
->Type
= EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE
;
139 Iso
->Length
= sizeof (*Iso
);
140 Iso
->Bus
= 0x00; // ISA
141 Iso
->Source
= 0x00; // IRQ0
142 Iso
->GlobalSystemInterruptVector
= 0x00000002;
143 Iso
->Flags
= 0x0000; // Conforms to specs of the bus
147 // Set Level-tiggered, Active High for all possible PCI link targets.
149 for (Loop
= 0; Loop
< 16; ++Loop
) {
150 if ((PcdGet16 (Pcd8259LegacyModeEdgeLevel
) & (1 << Loop
)) == 0) {
153 Iso
->Type
= EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE
;
154 Iso
->Length
= sizeof (*Iso
);
155 Iso
->Bus
= 0x00; // ISA
156 Iso
->Source
= (UINT8
) Loop
;
157 Iso
->GlobalSystemInterruptVector
= (UINT32
) Loop
;
158 Iso
->Flags
= 0x000D; // Level-tiggered, Active High
162 (UINTN
) (Iso
- (EFI_ACPI_1_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE
*)Ptr
) ==
168 LocalApicNmi
->Type
= EFI_ACPI_1_0_LOCAL_APIC_NMI
;
169 LocalApicNmi
->Length
= sizeof (*LocalApicNmi
);
170 LocalApicNmi
->AcpiProcessorId
= 0xFF; // applies to all processors
172 // polarity and trigger mode of the APIC I/O input signals conform to the
173 // specifications of the bus
175 LocalApicNmi
->Flags
= 0x0000;
177 // Local APIC interrupt input LINTn to which NMI is connected.
179 LocalApicNmi
->LocalApicInti
= 0x01;
180 Ptr
= LocalApicNmi
+ 1;
182 ASSERT ((UINTN
) ((UINT8
*)Ptr
- (UINT8
*)Madt
) == NewBufferSize
);
183 Status
= InstallAcpiTable (AcpiProtocol
, Madt
, NewBufferSize
, TableKey
);
200 PCI_WINDOW PciWindow32
;
201 PCI_WINDOW PciWindow64
;
216 AML_BYTE Pm1aCntSlpTyp
;
217 AML_BYTE Pm1bCntSlpTyp
;
218 AML_BYTE Reserved
[2];
219 } SYSTEM_STATE_PACKAGE
;
228 OUT FIRMWARE_DATA
*FwData
233 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*AllDesc
;
235 Status
= gDS
->GetMemorySpaceMap (&NumDesc
, &AllDesc
);
236 if (Status
== EFI_SUCCESS
) {
237 UINT64 NonMmio32MaxExclTop
;
238 UINT64 Mmio32MinBase
;
239 UINT64 Mmio32MaxExclTop
;
242 Status
= EFI_UNSUPPORTED
;
244 NonMmio32MaxExclTop
= 0;
245 Mmio32MinBase
= BASE_4GB
;
246 Mmio32MaxExclTop
= 0;
248 for (CurDesc
= 0; CurDesc
< NumDesc
; ++CurDesc
) {
249 CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*Desc
;
252 Desc
= &AllDesc
[CurDesc
];
253 ExclTop
= Desc
->BaseAddress
+ Desc
->Length
;
255 if (ExclTop
<= (UINT64
) PcdGet32 (PcdOvmfFdBaseAddress
)) {
256 switch (Desc
->GcdMemoryType
) {
257 case EfiGcdMemoryTypeNonExistent
:
260 case EfiGcdMemoryTypeReserved
:
261 case EfiGcdMemoryTypeSystemMemory
:
262 if (NonMmio32MaxExclTop
< ExclTop
) {
263 NonMmio32MaxExclTop
= ExclTop
;
267 case EfiGcdMemoryTypeMemoryMappedIo
:
268 if (Mmio32MinBase
> Desc
->BaseAddress
) {
269 Mmio32MinBase
= Desc
->BaseAddress
;
271 if (Mmio32MaxExclTop
< ExclTop
) {
272 Mmio32MaxExclTop
= ExclTop
;
282 if (Mmio32MinBase
< NonMmio32MaxExclTop
) {
283 Mmio32MinBase
= NonMmio32MaxExclTop
;
286 if (Mmio32MinBase
< Mmio32MaxExclTop
) {
287 FwData
->PciWindow32
.Base
= Mmio32MinBase
;
288 FwData
->PciWindow32
.End
= Mmio32MaxExclTop
- 1;
289 FwData
->PciWindow32
.Length
= Mmio32MaxExclTop
- Mmio32MinBase
;
291 FwData
->PciWindow64
.Base
= 0;
292 FwData
->PciWindow64
.End
= 0;
293 FwData
->PciWindow64
.Length
= 0;
295 Status
= EFI_SUCCESS
;
303 "ACPI PciWindow32: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
304 FwData
->PciWindow32
.Base
,
305 FwData
->PciWindow32
.End
,
306 FwData
->PciWindow32
.Length
310 "ACPI PciWindow64: Base=0x%08lx End=0x%08lx Length=0x%08lx\n",
311 FwData
->PciWindow64
.Base
,
312 FwData
->PciWindow64
.End
,
313 FwData
->PciWindow64
.Length
324 UINTN
*SuspendToRamSize
,
325 SYSTEM_STATE_PACKAGE
*SuspendToRam
,
326 UINTN
*SuspendToDiskSize
,
327 SYSTEM_STATE_PACKAGE
*SuspendToDisk
330 STATIC CONST SYSTEM_STATE_PACKAGE Template
= {
333 { '_', 'S', 'x', '_' }, // NameChar[4]
337 { 0x0A, 0x00 }, // Pm1aCntSlpTyp
338 { 0x0A, 0x00 }, // Pm1bCntSlpTyp -- we don't support it
344 RETURN_STATUS Status
;
345 FIRMWARE_CONFIG_ITEM FwCfgItem
;
347 UINT8 SystemStates
[6];
350 // configure defaults
352 *SuspendToRamSize
= sizeof Template
;
353 CopyMem (SuspendToRam
, &Template
, sizeof Template
);
354 SuspendToRam
->NameChar
[2] = '3'; // S3
355 SuspendToRam
->Pm1aCntSlpTyp
.ByteValue
= 1; // PIIX4: STR
357 *SuspendToDiskSize
= sizeof Template
;
358 CopyMem (SuspendToDisk
, &Template
, sizeof Template
);
359 SuspendToDisk
->NameChar
[2] = '4'; // S4
360 SuspendToDisk
->Pm1aCntSlpTyp
.ByteValue
= 2; // PIIX4: POSCL
363 // check for overrides
365 Status
= QemuFwCfgFindFile ("etc/system-states", &FwCfgItem
, &FwCfgSize
);
366 if (Status
!= RETURN_SUCCESS
|| FwCfgSize
!= sizeof SystemStates
) {
367 DEBUG ((DEBUG_INFO
, "ACPI using S3/S4 defaults\n"));
370 QemuFwCfgSelectItem (FwCfgItem
);
371 QemuFwCfgReadBytes (sizeof SystemStates
, SystemStates
);
374 // Each byte corresponds to a system state. In each byte, the MSB tells us
375 // whether the given state is enabled. If so, the three LSBs specify the
376 // value to be written to the PM control register's SUS_TYP bits.
378 if (SystemStates
[3] & BIT7
) {
379 SuspendToRam
->Pm1aCntSlpTyp
.ByteValue
=
380 SystemStates
[3] & (BIT2
| BIT1
| BIT0
);
381 DEBUG ((DEBUG_INFO
, "ACPI S3 value: %d\n",
382 SuspendToRam
->Pm1aCntSlpTyp
.ByteValue
));
384 *SuspendToRamSize
= 0;
385 DEBUG ((DEBUG_INFO
, "ACPI S3 disabled\n"));
388 if (SystemStates
[4] & BIT7
) {
389 SuspendToDisk
->Pm1aCntSlpTyp
.ByteValue
=
390 SystemStates
[4] & (BIT2
| BIT1
| BIT0
);
391 DEBUG ((DEBUG_INFO
, "ACPI S4 value: %d\n",
392 SuspendToDisk
->Pm1aCntSlpTyp
.ByteValue
));
394 *SuspendToDiskSize
= 0;
395 DEBUG ((DEBUG_INFO
, "ACPI S4 disabled\n"));
403 QemuInstallAcpiSsdtTable (
404 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
405 IN VOID
*AcpiTableBuffer
,
406 IN UINTN AcpiTableBufferSize
,
411 FIRMWARE_DATA
*FwData
;
413 Status
= EFI_OUT_OF_RESOURCES
;
415 FwData
= AllocateReservedPool (sizeof (*FwData
));
416 if (FwData
!= NULL
) {
417 UINTN SuspendToRamSize
;
418 SYSTEM_STATE_PACKAGE SuspendToRam
;
419 UINTN SuspendToDiskSize
;
420 SYSTEM_STATE_PACKAGE SuspendToDisk
;
424 GetSuspendStates (&SuspendToRamSize
, &SuspendToRam
,
425 &SuspendToDiskSize
, &SuspendToDisk
);
426 SsdtSize
= AcpiTableBufferSize
+ 17 + SuspendToRamSize
+ SuspendToDiskSize
;
427 Ssdt
= AllocatePool (SsdtSize
);
430 Status
= PopulateFwData (FwData
);
432 if (Status
== EFI_SUCCESS
) {
437 CopyMem (SsdtPtr
, AcpiTableBuffer
, AcpiTableBufferSize
);
438 SsdtPtr
+= AcpiTableBufferSize
;
441 // build "OperationRegion(FWDT, SystemMemory, 0x12345678, 0x87654321)"
443 *(SsdtPtr
++) = 0x5B; // ExtOpPrefix
444 *(SsdtPtr
++) = 0x80; // OpRegionOp
449 *(SsdtPtr
++) = 0x00; // SystemMemory
450 *(SsdtPtr
++) = 0x0C; // DWordPrefix
453 // no virtual addressing yet, take the four least significant bytes
455 CopyMem(SsdtPtr
, &FwData
, 4);
458 *(SsdtPtr
++) = 0x0C; // DWordPrefix
460 *(UINT32
*) SsdtPtr
= sizeof (*FwData
);
464 // add suspend system states
466 CopyMem (SsdtPtr
, &SuspendToRam
, SuspendToRamSize
);
467 SsdtPtr
+= SuspendToRamSize
;
468 CopyMem (SsdtPtr
, &SuspendToDisk
, SuspendToDiskSize
);
469 SsdtPtr
+= SuspendToDiskSize
;
471 ASSERT((UINTN
) (SsdtPtr
- Ssdt
) == SsdtSize
);
472 ((EFI_ACPI_DESCRIPTION_HEADER
*) Ssdt
)->Length
= (UINT32
) SsdtSize
;
473 Status
= InstallAcpiTable (AcpiProtocol
, Ssdt
, SsdtSize
, TableKey
);
479 if (Status
!= EFI_SUCCESS
) {
490 QemuInstallAcpiTable (
491 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
492 IN VOID
*AcpiTableBuffer
,
493 IN UINTN AcpiTableBufferSize
,
497 EFI_ACPI_DESCRIPTION_HEADER
*Hdr
;
498 EFI_ACPI_TABLE_INSTALL_ACPI_TABLE TableInstallFunction
;
500 Hdr
= (EFI_ACPI_DESCRIPTION_HEADER
*) AcpiTableBuffer
;
501 switch (Hdr
->Signature
) {
502 case EFI_ACPI_1_0_APIC_SIGNATURE
:
503 TableInstallFunction
= QemuInstallAcpiMadtTable
;
505 case EFI_ACPI_1_0_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
:
506 TableInstallFunction
= QemuInstallAcpiSsdtTable
;
509 TableInstallFunction
= InstallAcpiTable
;
512 return TableInstallFunction (
522 // The user structure for the ordered collection that will track the fw_cfg
523 // blobs under processing.
526 UINT8 File
[QEMU_LOADER_FNAME_SIZE
]; // NUL-terminated name of the fw_cfg
527 // blob. This is the ordering / search
529 UINTN Size
; // The number of bytes in this blob.
530 UINT8
*Base
; // Pointer to the blob data.
531 BOOLEAN HostsOnlyTableData
; // TRUE iff the blob has been found to
532 // only contain data that is directly
533 // part of ACPI tables.
538 Compare a standalone key against a user structure containing an embedded key.
540 @param[in] StandaloneKey Pointer to the bare key.
542 @param[in] UserStruct Pointer to the user structure with the embedded
545 @retval <0 If StandaloneKey compares less than UserStruct's key.
547 @retval 0 If StandaloneKey compares equal to UserStruct's key.
549 @retval >0 If StandaloneKey compares greater than UserStruct's key.
555 IN CONST VOID
*StandaloneKey
,
556 IN CONST VOID
*UserStruct
562 return AsciiStrCmp (StandaloneKey
, (CONST CHAR8
*)Blob
->File
);
567 Comparator function for two user structures.
569 @param[in] UserStruct1 Pointer to the first user structure.
571 @param[in] UserStruct2 Pointer to the second user structure.
573 @retval <0 If UserStruct1 compares less than UserStruct2.
575 @retval 0 If UserStruct1 compares equal to UserStruct2.
577 @retval >0 If UserStruct1 compares greater than UserStruct2.
583 IN CONST VOID
*UserStruct1
,
584 IN CONST VOID
*UserStruct2
590 return BlobKeyCompare (Blob1
->File
, UserStruct2
);
595 Process a QEMU_LOADER_ALLOCATE command.
597 @param[in] Allocate The QEMU_LOADER_ALLOCATE command to process.
599 @param[in,out] Tracker The ORDERED_COLLECTION tracking the BLOB user
600 structures created thus far.
602 @retval EFI_SUCCESS An area of whole AcpiNVS pages has been
603 allocated for the blob contents, and the
604 contents have been saved. A BLOB object (user
605 structure) has been allocated from pool memory,
606 referencing the blob contents. The BLOB user
607 structure has been linked into Tracker.
609 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
610 Allocate, or the Allocate command references a
611 file that is already known by Tracker.
613 @retval EFI_UNSUPPORTED Unsupported alignment request has been found in
616 @retval EFI_OUT_OF_RESOURCES Pool allocation failed.
618 @return Error codes from QemuFwCfgFindFile() and
619 gBS->AllocatePages().
625 IN CONST QEMU_LOADER_ALLOCATE
*Allocate
,
626 IN OUT ORDERED_COLLECTION
*Tracker
629 FIRMWARE_CONFIG_ITEM FwCfgItem
;
633 EFI_PHYSICAL_ADDRESS Address
;
636 if (Allocate
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
637 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
638 return EFI_PROTOCOL_ERROR
;
641 if (Allocate
->Alignment
> EFI_PAGE_SIZE
) {
642 DEBUG ((EFI_D_ERROR
, "%a: unsupported alignment 0x%x\n", __FUNCTION__
,
643 Allocate
->Alignment
));
644 return EFI_UNSUPPORTED
;
647 Status
= QemuFwCfgFindFile ((CHAR8
*)Allocate
->File
, &FwCfgItem
, &FwCfgSize
);
648 if (EFI_ERROR (Status
)) {
649 DEBUG ((EFI_D_ERROR
, "%a: QemuFwCfgFindFile(\"%a\"): %r\n", __FUNCTION__
,
650 Allocate
->File
, Status
));
654 NumPages
= EFI_SIZE_TO_PAGES (FwCfgSize
);
655 Address
= 0xFFFFFFFF;
656 Status
= gBS
->AllocatePages (AllocateMaxAddress
, EfiACPIMemoryNVS
, NumPages
,
658 if (EFI_ERROR (Status
)) {
662 Blob
= AllocatePool (sizeof *Blob
);
664 Status
= EFI_OUT_OF_RESOURCES
;
667 CopyMem (Blob
->File
, Allocate
->File
, QEMU_LOADER_FNAME_SIZE
);
668 Blob
->Size
= FwCfgSize
;
669 Blob
->Base
= (VOID
*)(UINTN
)Address
;
670 Blob
->HostsOnlyTableData
= TRUE
;
672 Status
= OrderedCollectionInsert (Tracker
, NULL
, Blob
);
673 if (Status
== RETURN_ALREADY_STARTED
) {
674 DEBUG ((EFI_D_ERROR
, "%a: duplicated file \"%a\"\n", __FUNCTION__
,
676 Status
= EFI_PROTOCOL_ERROR
;
678 if (EFI_ERROR (Status
)) {
682 QemuFwCfgSelectItem (FwCfgItem
);
683 QemuFwCfgReadBytes (FwCfgSize
, Blob
->Base
);
684 ZeroMem (Blob
->Base
+ Blob
->Size
, EFI_PAGES_TO_SIZE (NumPages
) - Blob
->Size
);
686 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" Alignment=0x%x Zone=%d Size=0x%Lx "
687 "Address=0x%Lx\n", __FUNCTION__
, Allocate
->File
, Allocate
->Alignment
,
688 Allocate
->Zone
, (UINT64
)Blob
->Size
, (UINT64
)(UINTN
)Blob
->Base
));
695 gBS
->FreePages (Address
, NumPages
);
702 Process a QEMU_LOADER_ADD_POINTER command.
704 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
706 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
707 structures created thus far.
709 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name(s) have been found in
710 AddPointer, or the AddPointer command references
711 a file unknown to Tracker, or the pointer to
712 relocate has invalid location, size, or value, or
713 the relocated pointer value is not representable
714 in the given pointer size.
716 @retval EFI_SUCCESS The pointer field inside the pointer blob has
722 ProcessCmdAddPointer (
723 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
724 IN CONST ORDERED_COLLECTION
*Tracker
727 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
732 if (AddPointer
->PointerFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0' ||
733 AddPointer
->PointeeFile
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
734 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
735 return EFI_PROTOCOL_ERROR
;
738 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
739 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
740 if (TrackerEntry
== NULL
|| TrackerEntry2
== NULL
) {
741 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference(s) \"%a\" / \"%a\"\n",
742 __FUNCTION__
, AddPointer
->PointerFile
, AddPointer
->PointeeFile
));
743 return EFI_PROTOCOL_ERROR
;
746 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
747 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
748 if ((AddPointer
->PointerSize
!= 1 && AddPointer
->PointerSize
!= 2 &&
749 AddPointer
->PointerSize
!= 4 && AddPointer
->PointerSize
!= 8) ||
750 Blob
->Size
< AddPointer
->PointerSize
||
751 Blob
->Size
- AddPointer
->PointerSize
< AddPointer
->PointerOffset
) {
752 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer location or size in \"%a\"\n",
753 __FUNCTION__
, AddPointer
->PointerFile
));
754 return EFI_PROTOCOL_ERROR
;
757 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
759 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
760 if (PointerValue
>= Blob2
->Size
) {
761 DEBUG ((EFI_D_ERROR
, "%a: invalid pointer value in \"%a\"\n", __FUNCTION__
,
762 AddPointer
->PointerFile
));
763 return EFI_PROTOCOL_ERROR
;
767 // The memory allocation system ensures that the address of the byte past the
768 // last byte of any allocated object is expressible (no wraparound).
770 ASSERT ((UINTN
)Blob2
->Base
<= MAX_ADDRESS
- Blob2
->Size
);
772 PointerValue
+= (UINT64
)(UINTN
)Blob2
->Base
;
774 RShiftU64 (PointerValue
, AddPointer
->PointerSize
* 8 - 1), 1) != 0) {
775 DEBUG ((EFI_D_ERROR
, "%a: relocated pointer value unrepresentable in "
776 "\"%a\"\n", __FUNCTION__
, AddPointer
->PointerFile
));
777 return EFI_PROTOCOL_ERROR
;
780 CopyMem (PointerField
, &PointerValue
, AddPointer
->PointerSize
);
782 DEBUG ((EFI_D_VERBOSE
, "%a: PointerFile=\"%a\" PointeeFile=\"%a\" "
783 "PointerOffset=0x%x PointerSize=%d\n", __FUNCTION__
,
784 AddPointer
->PointerFile
, AddPointer
->PointeeFile
,
785 AddPointer
->PointerOffset
, AddPointer
->PointerSize
));
791 Process a QEMU_LOADER_ADD_CHECKSUM command.
793 @param[in] AddChecksum The QEMU_LOADER_ADD_CHECKSUM command to process.
795 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
796 structures created thus far.
798 @retval EFI_PROTOCOL_ERROR Malformed fw_cfg file name has been found in
799 AddChecksum, or the AddChecksum command
800 references a file unknown to Tracker, or the
801 range to checksum is invalid.
803 @retval EFI_SUCCESS The requested range has been checksummed.
808 ProcessCmdAddChecksum (
809 IN CONST QEMU_LOADER_ADD_CHECKSUM
*AddChecksum
,
810 IN CONST ORDERED_COLLECTION
*Tracker
813 ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
816 if (AddChecksum
->File
[QEMU_LOADER_FNAME_SIZE
- 1] != '\0') {
817 DEBUG ((EFI_D_ERROR
, "%a: malformed file name\n", __FUNCTION__
));
818 return EFI_PROTOCOL_ERROR
;
821 TrackerEntry
= OrderedCollectionFind (Tracker
, AddChecksum
->File
);
822 if (TrackerEntry
== NULL
) {
823 DEBUG ((EFI_D_ERROR
, "%a: invalid blob reference \"%a\"\n", __FUNCTION__
,
825 return EFI_PROTOCOL_ERROR
;
828 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
829 if (Blob
->Size
<= AddChecksum
->ResultOffset
||
830 Blob
->Size
< AddChecksum
->Length
||
831 Blob
->Size
- AddChecksum
->Length
< AddChecksum
->Start
) {
832 DEBUG ((EFI_D_ERROR
, "%a: invalid checksum range in \"%a\"\n",
833 __FUNCTION__
, AddChecksum
->File
));
834 return EFI_PROTOCOL_ERROR
;
837 Blob
->Base
[AddChecksum
->ResultOffset
] = CalculateCheckSum8 (
838 Blob
->Base
+ AddChecksum
->Start
,
841 DEBUG ((EFI_D_VERBOSE
, "%a: File=\"%a\" ResultOffset=0x%x Start=0x%x "
842 "Length=0x%x\n", __FUNCTION__
, AddChecksum
->File
,
843 AddChecksum
->ResultOffset
, AddChecksum
->Start
, AddChecksum
->Length
));
849 // We'll be saving the keys of installed tables so that we can roll them back
850 // in case of failure. 128 tables should be enough for anyone (TM).
852 #define INSTALLED_TABLES_MAX 128
855 Process a QEMU_LOADER_ADD_POINTER command in order to see if its target byte
856 array is an ACPI table, and if so, install it.
858 This function assumes that the entire QEMU linker/loader command file has
859 been processed successfuly in a prior first pass.
861 @param[in] AddPointer The QEMU_LOADER_ADD_POINTER command to process.
863 @param[in] Tracker The ORDERED_COLLECTION tracking the BLOB user
866 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
868 @param[in,out] InstalledKey On input, an array of INSTALLED_TABLES_MAX UINTN
869 elements, allocated by the caller. On output,
870 the function will have stored (appended) the
871 AcpiProtocol-internal key of the ACPI table that
872 the function has installed, if the AddPointer
873 command identified an ACPI table that is
874 different from RSDT and XSDT.
876 @param[in,out] NumInstalled On input, the number of entries already used in
877 InstalledKey; it must be in [0,
878 INSTALLED_TABLES_MAX] inclusive. On output, the
879 parameter is incremented if the AddPointer
880 command identified an ACPI table that is
881 different from RSDT and XSDT.
883 @retval EFI_INVALID_PARAMETER NumInstalled was outside the allowed range on
886 @retval EFI_OUT_OF_RESOURCES The AddPointer command identified an ACPI
887 table different from RSDT and XSDT, but there
888 was no more room in InstalledKey.
890 @retval EFI_SUCCESS AddPointer has been processed. Either an ACPI
891 table different from RSDT and XSDT has been
892 installed (reflected by InstalledKey and
893 NumInstalled), or RSDT or XSDT has been
894 identified but not installed, or the fw_cfg
895 blob pointed-into by AddPointer has been
896 marked as hosting something else than just
897 direct ACPI table contents.
899 @return Error codes returned by
900 AcpiProtocol->InstallAcpiTable().
905 Process2ndPassCmdAddPointer (
906 IN CONST QEMU_LOADER_ADD_POINTER
*AddPointer
,
907 IN CONST ORDERED_COLLECTION
*Tracker
,
908 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
,
909 IN OUT UINTN InstalledKey
[INSTALLED_TABLES_MAX
],
910 IN OUT INT32
*NumInstalled
913 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry
;
914 CONST ORDERED_COLLECTION_ENTRY
*TrackerEntry2
;
917 CONST UINT8
*PointerField
;
919 UINTN Blob2Remaining
;
921 CONST EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*Facs
;
922 CONST EFI_ACPI_DESCRIPTION_HEADER
*Header
;
925 if (*NumInstalled
< 0 || *NumInstalled
> INSTALLED_TABLES_MAX
) {
926 return EFI_INVALID_PARAMETER
;
929 TrackerEntry
= OrderedCollectionFind (Tracker
, AddPointer
->PointerFile
);
930 TrackerEntry2
= OrderedCollectionFind (Tracker
, AddPointer
->PointeeFile
);
931 Blob
= OrderedCollectionUserStruct (TrackerEntry
);
932 Blob2
= OrderedCollectionUserStruct (TrackerEntry2
);
933 PointerField
= Blob
->Base
+ AddPointer
->PointerOffset
;
935 CopyMem (&PointerValue
, PointerField
, AddPointer
->PointerSize
);
938 // We assert that PointerValue falls inside Blob2's contents. This is ensured
939 // by the Blob2->Size check and later checks in ProcessCmdAddPointer().
941 Blob2Remaining
= (UINTN
)Blob2
->Base
;
942 ASSERT(PointerValue
>= Blob2Remaining
);
943 Blob2Remaining
+= Blob2
->Size
;
944 ASSERT (PointerValue
< Blob2Remaining
);
946 Blob2Remaining
-= (UINTN
) PointerValue
;
947 DEBUG ((EFI_D_VERBOSE
, "%a: checking for ACPI header in \"%a\" at 0x%Lx "
948 "(remaining: 0x%Lx): ", __FUNCTION__
, AddPointer
->PointeeFile
,
949 PointerValue
, (UINT64
)Blob2Remaining
));
954 // To make our job simple, the FACS has a custom header. Sigh.
956 if (sizeof *Facs
<= Blob2Remaining
) {
957 Facs
= (EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE
*)(UINTN
)PointerValue
;
959 if (Facs
->Length
>= sizeof *Facs
&&
960 Facs
->Length
<= Blob2Remaining
&&
962 EFI_ACPI_1_0_FIRMWARE_ACPI_CONTROL_STRUCTURE_SIGNATURE
) {
963 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
964 (CONST CHAR8
*)&Facs
->Signature
, Facs
->Length
));
965 TableSize
= Facs
->Length
;
970 // check for the uniform tables
972 if (TableSize
== 0 && sizeof *Header
<= Blob2Remaining
) {
973 Header
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
)PointerValue
;
975 if (Header
->Length
>= sizeof *Header
&&
976 Header
->Length
<= Blob2Remaining
&&
977 CalculateSum8 ((CONST UINT8
*)Header
, Header
->Length
) == 0) {
979 // This looks very much like an ACPI table from QEMU:
980 // - Length field consistent with both ACPI and containing blob size
981 // - checksum is correct
983 DEBUG ((EFI_D_VERBOSE
, "found \"%-4.4a\" size 0x%x\n",
984 (CONST CHAR8
*)&Header
->Signature
, Header
->Length
));
985 TableSize
= Header
->Length
;
988 // Skip RSDT and XSDT because those are handled by
989 // EFI_ACPI_TABLE_PROTOCOL automatically.
990 if (Header
->Signature
==
991 EFI_ACPI_1_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
||
993 EFI_ACPI_2_0_EXTENDED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
999 if (TableSize
== 0) {
1000 DEBUG ((EFI_D_VERBOSE
, "not found; marking fw_cfg blob as opaque\n"));
1001 Blob2
->HostsOnlyTableData
= FALSE
;
1005 if (*NumInstalled
== INSTALLED_TABLES_MAX
) {
1006 DEBUG ((EFI_D_ERROR
, "%a: can't install more than %d tables\n",
1007 __FUNCTION__
, INSTALLED_TABLES_MAX
));
1008 return EFI_OUT_OF_RESOURCES
;
1011 Status
= AcpiProtocol
->InstallAcpiTable (AcpiProtocol
,
1012 (VOID
*)(UINTN
)PointerValue
, TableSize
,
1013 &InstalledKey
[*NumInstalled
]);
1014 if (EFI_ERROR (Status
)) {
1015 DEBUG ((EFI_D_ERROR
, "%a: InstallAcpiTable(): %r\n", __FUNCTION__
,
1025 Download, process, and install ACPI table data from the QEMU loader
1028 @param[in] AcpiProtocol The ACPI table protocol used to install tables.
1030 @retval EFI_UNSUPPORTED Firmware configuration is unavailable, or QEMU
1031 loader command with unsupported parameters
1034 @retval EFI_NOT_FOUND The host doesn't export the required fw_cfg
1037 @retval EFI_OUT_OF_RESOURCES Memory allocation failed, or more than
1038 INSTALLED_TABLES_MAX tables found.
1040 @retval EFI_PROTOCOL_ERROR Found invalid fw_cfg contents.
1042 @return Status codes returned by
1043 AcpiProtocol->InstallAcpiTable().
1048 InstallAllQemuLinkedTables (
1049 IN EFI_ACPI_TABLE_PROTOCOL
*AcpiProtocol
1053 FIRMWARE_CONFIG_ITEM FwCfgItem
;
1055 QEMU_LOADER_ENTRY
*LoaderStart
;
1056 CONST QEMU_LOADER_ENTRY
*LoaderEntry
, *LoaderEnd
;
1057 ORDERED_COLLECTION
*Tracker
;
1058 UINTN
*InstalledKey
;
1060 ORDERED_COLLECTION_ENTRY
*TrackerEntry
, *TrackerEntry2
;
1062 Status
= QemuFwCfgFindFile ("etc/table-loader", &FwCfgItem
, &FwCfgSize
);
1063 if (EFI_ERROR (Status
)) {
1066 if (FwCfgSize
% sizeof *LoaderEntry
!= 0) {
1067 DEBUG ((EFI_D_ERROR
, "%a: \"etc/table-loader\" has invalid size 0x%Lx\n",
1068 __FUNCTION__
, (UINT64
)FwCfgSize
));
1069 return EFI_PROTOCOL_ERROR
;
1072 LoaderStart
= AllocatePool (FwCfgSize
);
1073 if (LoaderStart
== NULL
) {
1074 return EFI_OUT_OF_RESOURCES
;
1076 QemuFwCfgSelectItem (FwCfgItem
);
1077 QemuFwCfgReadBytes (FwCfgSize
, LoaderStart
);
1078 LoaderEnd
= LoaderStart
+ FwCfgSize
/ sizeof *LoaderEntry
;
1080 Tracker
= OrderedCollectionInit (BlobCompare
, BlobKeyCompare
);
1081 if (Tracker
== NULL
) {
1082 Status
= EFI_OUT_OF_RESOURCES
;
1087 // first pass: process the commands
1089 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1090 switch (LoaderEntry
->Type
) {
1091 case QemuLoaderCmdAllocate
:
1092 Status
= ProcessCmdAllocate (&LoaderEntry
->Command
.Allocate
, Tracker
);
1095 case QemuLoaderCmdAddPointer
:
1096 Status
= ProcessCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
1100 case QemuLoaderCmdAddChecksum
:
1101 Status
= ProcessCmdAddChecksum (&LoaderEntry
->Command
.AddChecksum
,
1106 DEBUG ((EFI_D_VERBOSE
, "%a: unknown loader command: 0x%x\n",
1107 __FUNCTION__
, LoaderEntry
->Type
));
1111 if (EFI_ERROR (Status
)) {
1116 InstalledKey
= AllocatePool (INSTALLED_TABLES_MAX
* sizeof *InstalledKey
);
1117 if (InstalledKey
== NULL
) {
1118 Status
= EFI_OUT_OF_RESOURCES
;
1123 // second pass: identify and install ACPI tables
1126 for (LoaderEntry
= LoaderStart
; LoaderEntry
< LoaderEnd
; ++LoaderEntry
) {
1127 if (LoaderEntry
->Type
== QemuLoaderCmdAddPointer
) {
1128 Status
= Process2ndPassCmdAddPointer (&LoaderEntry
->Command
.AddPointer
,
1129 Tracker
, AcpiProtocol
, InstalledKey
, &Installed
);
1130 if (EFI_ERROR (Status
)) {
1136 if (EFI_ERROR (Status
)) {
1138 // roll back partial installation
1140 while (Installed
> 0) {
1142 AcpiProtocol
->UninstallAcpiTable (AcpiProtocol
, InstalledKey
[Installed
]);
1145 DEBUG ((EFI_D_INFO
, "%a: installed %d tables\n", __FUNCTION__
, Installed
));
1148 FreePool (InstalledKey
);
1152 // Tear down the tracker infrastructure. Each fw_cfg blob will be left in
1153 // place only if we're exiting with success and the blob hosts data that is
1154 // not directly part of some ACPI table.
1156 for (TrackerEntry
= OrderedCollectionMin (Tracker
); TrackerEntry
!= NULL
;
1157 TrackerEntry
= TrackerEntry2
) {
1161 TrackerEntry2
= OrderedCollectionNext (TrackerEntry
);
1162 OrderedCollectionDelete (Tracker
, TrackerEntry
, &UserStruct
);
1165 if (EFI_ERROR (Status
) || Blob
->HostsOnlyTableData
) {
1166 DEBUG ((EFI_D_VERBOSE
, "%a: freeing \"%a\"\n", __FUNCTION__
,
1168 gBS
->FreePages ((UINTN
)Blob
->Base
, EFI_SIZE_TO_PAGES (Blob
->Size
));
1172 OrderedCollectionUninit (Tracker
);
1175 FreePool (LoaderStart
);