mirror of https://github.com/acidanthera/audk.git
368 lines
10 KiB
C
368 lines
10 KiB
C
/** @file
|
|
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "DmaProtection.h"
|
|
|
|
EFI_ACPI_SDT_PROTOCOL *mAcpiSdt;
|
|
UINT64 mBelow4GMemoryLimit;
|
|
UINT64 mAbove4GMemoryLimit;
|
|
|
|
EDKII_PLATFORM_VTD_POLICY_PROTOCOL *mPlatformVTdPolicy;
|
|
|
|
/**
|
|
return the UEFI memory information.
|
|
|
|
@param[out] Below4GMemoryLimit The below 4GiB memory limit
|
|
@param[out] Above4GMemoryLimit The above 4GiB memory limit
|
|
**/
|
|
VOID
|
|
ReturnUefiMemoryMap (
|
|
OUT UINT64 *Below4GMemoryLimit,
|
|
OUT UINT64 *Above4GMemoryLimit
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_MEMORY_DESCRIPTOR *EfiMemoryMap;
|
|
EFI_MEMORY_DESCRIPTOR *EfiMemoryMapEnd;
|
|
EFI_MEMORY_DESCRIPTOR *EfiEntry;
|
|
EFI_MEMORY_DESCRIPTOR *NextEfiEntry;
|
|
EFI_MEMORY_DESCRIPTOR TempEfiEntry;
|
|
UINTN EfiMemoryMapSize;
|
|
UINTN EfiMapKey;
|
|
UINTN EfiDescriptorSize;
|
|
UINT32 EfiDescriptorVersion;
|
|
UINT64 MemoryBlockLength;
|
|
|
|
*Below4GMemoryLimit = 0;
|
|
*Above4GMemoryLimit = 0;
|
|
|
|
//
|
|
// Get the EFI memory map.
|
|
//
|
|
EfiMemoryMapSize = 0;
|
|
EfiMemoryMap = NULL;
|
|
Status = gBS->GetMemoryMap (
|
|
&EfiMemoryMapSize,
|
|
EfiMemoryMap,
|
|
&EfiMapKey,
|
|
&EfiDescriptorSize,
|
|
&EfiDescriptorVersion
|
|
);
|
|
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
|
|
|
|
do {
|
|
//
|
|
// Use size returned back plus 1 descriptor for the AllocatePool.
|
|
// We don't just multiply by 2 since the "for" loop below terminates on
|
|
// EfiMemoryMapEnd which is dependent upon EfiMemoryMapSize. Otherwize
|
|
// we process bogus entries and create bogus E820 entries.
|
|
//
|
|
EfiMemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (EfiMemoryMapSize);
|
|
ASSERT (EfiMemoryMap != NULL);
|
|
Status = gBS->GetMemoryMap (
|
|
&EfiMemoryMapSize,
|
|
EfiMemoryMap,
|
|
&EfiMapKey,
|
|
&EfiDescriptorSize,
|
|
&EfiDescriptorVersion
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (EfiMemoryMap);
|
|
}
|
|
} while (Status == EFI_BUFFER_TOO_SMALL);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Sort memory map from low to high
|
|
//
|
|
EfiEntry = EfiMemoryMap;
|
|
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
|
|
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
|
|
while (EfiEntry < EfiMemoryMapEnd) {
|
|
while (NextEfiEntry < EfiMemoryMapEnd) {
|
|
if (EfiEntry->PhysicalStart > NextEfiEntry->PhysicalStart) {
|
|
CopyMem (&TempEfiEntry, EfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
|
|
CopyMem (EfiEntry, NextEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
|
|
CopyMem (NextEfiEntry, &TempEfiEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
|
|
}
|
|
|
|
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (NextEfiEntry, EfiDescriptorSize);
|
|
}
|
|
|
|
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
|
|
NextEfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
DEBUG ((DEBUG_INFO, "MemoryMap:\n"));
|
|
EfiEntry = EfiMemoryMap;
|
|
EfiMemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) EfiMemoryMap + EfiMemoryMapSize);
|
|
while (EfiEntry < EfiMemoryMapEnd) {
|
|
MemoryBlockLength = (UINT64) (LShiftU64 (EfiEntry->NumberOfPages, 12));
|
|
DEBUG ((DEBUG_INFO, "Entry(0x%02x) 0x%016lx - 0x%016lx\n", EfiEntry->Type, EfiEntry->PhysicalStart, EfiEntry->PhysicalStart + MemoryBlockLength));
|
|
switch (EfiEntry->Type) {
|
|
case EfiLoaderCode:
|
|
case EfiLoaderData:
|
|
case EfiBootServicesCode:
|
|
case EfiBootServicesData:
|
|
case EfiConventionalMemory:
|
|
case EfiRuntimeServicesCode:
|
|
case EfiRuntimeServicesData:
|
|
case EfiACPIReclaimMemory:
|
|
case EfiACPIMemoryNVS:
|
|
case EfiReservedMemoryType:
|
|
if ((EfiEntry->PhysicalStart + MemoryBlockLength) <= BASE_1MB) {
|
|
//
|
|
// Skip the memory block is under 1MB
|
|
//
|
|
} else if (EfiEntry->PhysicalStart >= BASE_4GB) {
|
|
if (*Above4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
|
|
*Above4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
|
|
}
|
|
} else {
|
|
if (*Below4GMemoryLimit < EfiEntry->PhysicalStart + MemoryBlockLength) {
|
|
*Below4GMemoryLimit = EfiEntry->PhysicalStart + MemoryBlockLength;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
EfiEntry = NEXT_MEMORY_DESCRIPTOR (EfiEntry, EfiDescriptorSize);
|
|
}
|
|
|
|
FreePool (EfiMemoryMap);
|
|
|
|
DEBUG ((DEBUG_INFO, "Result:\n"));
|
|
DEBUG ((DEBUG_INFO, "Below4GMemoryLimit: 0x%016lx\n", *Below4GMemoryLimit));
|
|
DEBUG ((DEBUG_INFO, "Above4GMemoryLimit: 0x%016lx\n", *Above4GMemoryLimit));
|
|
|
|
return ;
|
|
}
|
|
|
|
/**
|
|
Initialize platform VTd policy.
|
|
**/
|
|
VOID
|
|
InitializePlatformVTdPolicy (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN DeviceInfoCount;
|
|
EDKII_PLATFORM_VTD_DEVICE_INFO *DeviceInfo;
|
|
UINTN Index;
|
|
|
|
//
|
|
// It is optional.
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEdkiiPlatformVTdPolicyProtocolGuid,
|
|
NULL,
|
|
(VOID **)&mPlatformVTdPolicy
|
|
);
|
|
if (!EFI_ERROR(Status)) {
|
|
Status = mPlatformVTdPolicy->GetExceptionDeviceList (mPlatformVTdPolicy, &DeviceInfoCount, &DeviceInfo);
|
|
if (!EFI_ERROR(Status)) {
|
|
for (Index = 0; Index < DeviceInfoCount; Index++) {
|
|
AlwaysEnablePageAttribute (DeviceInfo[Index].Segment, DeviceInfo[Index].SourceId);
|
|
}
|
|
FreePool (DeviceInfo);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Setup VTd engine.
|
|
**/
|
|
VOID
|
|
SetupVtd (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
VOID *PciEnumerationComplete;
|
|
UINTN Index;
|
|
UINT64 Below4GMemoryLimit;
|
|
UINT64 Above4GMemoryLimit;
|
|
|
|
//
|
|
// PCI Enumeration must be done
|
|
//
|
|
Status = gBS->LocateProtocol (
|
|
&gEfiPciEnumerationCompleteProtocolGuid,
|
|
NULL,
|
|
&PciEnumerationComplete
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
ReturnUefiMemoryMap (&Below4GMemoryLimit, &Above4GMemoryLimit);
|
|
Below4GMemoryLimit = ALIGN_VALUE_UP(Below4GMemoryLimit, SIZE_256MB);
|
|
DEBUG ((DEBUG_INFO, " Adjusted Below4GMemoryLimit: 0x%016lx\n", Below4GMemoryLimit));
|
|
|
|
mBelow4GMemoryLimit = Below4GMemoryLimit;
|
|
mAbove4GMemoryLimit = Above4GMemoryLimit;
|
|
|
|
//
|
|
// 1. setup
|
|
//
|
|
DEBUG ((DEBUG_INFO, "GetDmarAcpiTable\n"));
|
|
Status = GetDmarAcpiTable ();
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "ParseDmarAcpiTable\n"));
|
|
Status = ParseDmarAcpiTableDrhd ();
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "PrepareVtdConfig\n"));
|
|
PrepareVtdConfig ();
|
|
|
|
//
|
|
// 2. initialization
|
|
//
|
|
DEBUG ((DEBUG_INFO, "SetupTranslationTable\n"));
|
|
Status = SetupTranslationTable ();
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
InitializePlatformVTdPolicy ();
|
|
|
|
ParseDmarAcpiTableRmrr ();
|
|
|
|
for (Index = 0; Index < mVtdUnitNumber; Index++) {
|
|
DEBUG ((DEBUG_INFO,"VTD Unit %d (Segment: %04x)\n", Index, mVtdUnitInformation[Index].Segment));
|
|
if (mVtdUnitInformation[Index].ExtRootEntryTable != NULL) {
|
|
DumpDmarExtContextEntryTable (mVtdUnitInformation[Index].ExtRootEntryTable);
|
|
}
|
|
if (mVtdUnitInformation[Index].RootEntryTable != NULL) {
|
|
DumpDmarContextEntryTable (mVtdUnitInformation[Index].RootEntryTable);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3. enable
|
|
//
|
|
DEBUG ((DEBUG_INFO, "EnableDmar\n"));
|
|
Status = EnableDmar ();
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
DEBUG ((DEBUG_INFO, "DumpVtdRegs\n"));
|
|
DumpVtdRegsAll ();
|
|
}
|
|
|
|
/**
|
|
ACPI notification function.
|
|
|
|
@param[in] Table A pointer to the ACPI table header.
|
|
@param[in] Version The ACPI table's version.
|
|
@param[in] TableKey The table key for this ACPI table.
|
|
|
|
@retval EFI_SUCCESS The notification function is executed.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
AcpiNotificationFunc (
|
|
IN EFI_ACPI_SDT_HEADER *Table,
|
|
IN EFI_ACPI_TABLE_VERSION Version,
|
|
IN UINTN TableKey
|
|
)
|
|
{
|
|
if (Table->Signature == EFI_ACPI_4_0_DMA_REMAPPING_TABLE_SIGNATURE) {
|
|
DEBUG((DEBUG_INFO, "Vtd AcpiNotificationFunc\n"));
|
|
SetupVtd ();
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Exit boot service callback function.
|
|
|
|
@param[in] Event The event handle.
|
|
@param[in] Context The event content.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
OnExitBootServices (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_INFO, "Vtd OnExitBootServices\n"));
|
|
DumpVtdRegsAll ();
|
|
DisableDmar ();
|
|
DumpVtdRegsAll ();
|
|
}
|
|
|
|
/**
|
|
Legacy boot callback function.
|
|
|
|
@param[in] Event The event handle.
|
|
@param[in] Context The event content.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
OnLegacyBoot (
|
|
EFI_EVENT Event,
|
|
VOID *Context
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_INFO, "Vtd OnLegacyBoot\n"));
|
|
DumpVtdRegsAll ();
|
|
DisableDmar ();
|
|
DumpVtdRegsAll ();
|
|
}
|
|
|
|
/**
|
|
Initialize DMA protection.
|
|
**/
|
|
VOID
|
|
InitializeDmaProtection (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_EVENT ExitBootServicesEvent;
|
|
EFI_EVENT LegacyBootEvent;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (VOID **) &mAcpiSdt);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = mAcpiSdt->RegisterNotify (TRUE, AcpiNotificationFunc);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
OnExitBootServices,
|
|
NULL,
|
|
&gEfiEventExitBootServicesGuid,
|
|
&ExitBootServicesEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = EfiCreateEventLegacyBootEx (
|
|
TPL_NOTIFY,
|
|
OnLegacyBoot,
|
|
NULL,
|
|
&LegacyBootEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return ;
|
|
}
|