You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
9.9 KiB
322 lines
9.9 KiB
/** @file
|
|
Instance of Runtime PCI Segment Library that support multi-segment PCI configuration access.
|
|
|
|
PCI Segment Library that consumes segment information provided by PciSegmentInfoLib to
|
|
support multi-segment PCI configuration access through enhanced configuration access mechanism.
|
|
|
|
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 "PciSegmentLibCommon.h"
|
|
#include <PiDxe.h>
|
|
#include <Guid/EventGroup.h>
|
|
#include <Library/UefiRuntimeLib.h>
|
|
#include <Library/DxeServicesTableLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/PciSegmentInfoLib.h>
|
|
|
|
///
|
|
/// Define table for mapping PCI Segment MMIO physical addresses to virtual addresses at OS runtime
|
|
///
|
|
typedef struct {
|
|
UINTN PhysicalAddress;
|
|
UINTN VirtualAddress;
|
|
} PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE;
|
|
|
|
///
|
|
/// Set Virtual Address Map Event
|
|
///
|
|
EFI_EVENT mDxeRuntimePciSegmentLibVirtualNotifyEvent = NULL;
|
|
|
|
///
|
|
/// The number of PCI devices that have been registered for runtime access.
|
|
///
|
|
UINTN mDxeRuntimePciSegmentLibNumberOfRuntimeRanges = 0;
|
|
|
|
///
|
|
/// The table of PCI devices that have been registered for runtime access.
|
|
///
|
|
PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE *mDxeRuntimePciSegmentLibRegistrationTable = NULL;
|
|
|
|
///
|
|
/// The table index of the most recent virtual address lookup.
|
|
///
|
|
UINTN mDxeRuntimePciSegmentLibLastRuntimeRange = 0;
|
|
|
|
/**
|
|
Convert the physical PCI Express MMIO addresses for all registered PCI devices
|
|
to virtual addresses.
|
|
|
|
@param[in] Event The event that is being processed.
|
|
@param[in] Context The Event Context.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DxeRuntimePciSegmentLibVirtualNotify (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// If there have been no runtime registrations, then just return
|
|
//
|
|
if (mDxeRuntimePciSegmentLibRegistrationTable == NULL) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Convert physical addresses associated with the set of registered PCI devices to
|
|
// virtual addresses.
|
|
//
|
|
for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
|
|
Status = EfiConvertPointer (0, (VOID **) &(mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress));
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Convert table pointer that is allocated from EfiRuntimeServicesData to a virtual address.
|
|
//
|
|
Status = EfiConvertPointer (0, (VOID **) &mDxeRuntimePciSegmentLibRegistrationTable);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
/**
|
|
The constructor function caches the PCI Express Base Address and creates a
|
|
Set Virtual Address Map event to convert physical address to virtual addresses.
|
|
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The constructor completed successfully.
|
|
@retval Other value The constructor did not complete successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
DxeRuntimePciSegmentLibConstructor (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Register SetVirtualAddressMap () notify function
|
|
//
|
|
Status = gBS->CreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
DxeRuntimePciSegmentLibVirtualNotify,
|
|
NULL,
|
|
&gEfiEventVirtualAddressChangeGuid,
|
|
&mDxeRuntimePciSegmentLibVirtualNotifyEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
The destructor function frees any allocated buffers and closes the Set Virtual
|
|
Address Map event.
|
|
|
|
@param ImageHandle The firmware allocated handle for the EFI image.
|
|
@param SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The destructor completed successfully.
|
|
@retval Other value The destructor did not complete successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
DxeRuntimePciSegmentLibDestructor (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// If one or more PCI devices have been registered for runtime access, then
|
|
// free the registration table.
|
|
//
|
|
if (mDxeRuntimePciSegmentLibRegistrationTable != NULL) {
|
|
FreePool (mDxeRuntimePciSegmentLibRegistrationTable);
|
|
}
|
|
|
|
//
|
|
// Close the Set Virtual Address Map event
|
|
//
|
|
Status = gBS->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Register a PCI device so PCI configuration registers may be accessed after
|
|
SetVirtualAddressMap().
|
|
|
|
If any reserved bits in Address are set, then ASSERT().
|
|
|
|
@param Address The address that encodes the PCI Bus, Device, Function and
|
|
Register.
|
|
|
|
@retval RETURN_SUCCESS The PCI device was registered for runtime access.
|
|
@retval RETURN_UNSUPPORTED An attempt was made to call this function
|
|
after ExitBootServices().
|
|
@retval RETURN_UNSUPPORTED The resources required to access the PCI device
|
|
at runtime could not be mapped.
|
|
@retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
|
|
complete the registration.
|
|
|
|
**/
|
|
RETURN_STATUS
|
|
EFIAPI
|
|
PciSegmentRegisterForRuntimeAccess (
|
|
IN UINTN Address
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
|
|
UINTN Index;
|
|
VOID *NewTable;
|
|
UINTN Count;
|
|
PCI_SEGMENT_INFO *SegmentInfo;
|
|
UINT64 EcamAddress;
|
|
|
|
//
|
|
// Convert Address to a ECAM address at the beginning of the PCI Configuration
|
|
// header for the specified PCI Bus/Dev/Func
|
|
//
|
|
Address &= ~(UINTN)EFI_PAGE_MASK;
|
|
SegmentInfo = GetPciSegmentInfo (&Count);
|
|
EcamAddress = PciSegmentLibGetEcamAddress (Address, SegmentInfo, Count);
|
|
|
|
//
|
|
// Return an error if this function is called after ExitBootServices().
|
|
//
|
|
if (EfiAtRuntime ()) {
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
if (sizeof (UINTN) == sizeof (UINT32)) {
|
|
ASSERT (EcamAddress < BASE_4GB);
|
|
}
|
|
Address = (UINTN)EcamAddress;
|
|
|
|
//
|
|
// See if Address has already been registerd for runtime access
|
|
//
|
|
for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
|
|
if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == Address) {
|
|
return RETURN_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the GCD Memory Descriptor for the ECAM Address
|
|
//
|
|
Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
|
|
if (EFI_ERROR (Status)) {
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Mark the 4KB region for the PCI Express Bus/Dev/Func as EFI_RUNTIME_MEMORY so the OS
|
|
// will allocate a virtual address range for the 4KB PCI Configuration Header.
|
|
//
|
|
Status = gDS->SetMemorySpaceAttributes (Address, EFI_PAGE_SIZE, Descriptor.Attributes | EFI_MEMORY_RUNTIME);
|
|
if (EFI_ERROR (Status)) {
|
|
return RETURN_UNSUPPORTED;
|
|
}
|
|
|
|
//
|
|
// Grow the size of the registration table
|
|
//
|
|
NewTable = ReallocateRuntimePool (
|
|
(mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
|
|
(mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
|
|
mDxeRuntimePciSegmentLibRegistrationTable
|
|
);
|
|
if (NewTable == NULL) {
|
|
return RETURN_OUT_OF_RESOURCES;
|
|
}
|
|
mDxeRuntimePciSegmentLibRegistrationTable = NewTable;
|
|
mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].PhysicalAddress = Address;
|
|
mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].VirtualAddress = Address;
|
|
mDxeRuntimePciSegmentLibNumberOfRuntimeRanges++;
|
|
|
|
return RETURN_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Return the linear address for the physical address.
|
|
|
|
@param Address The physical address.
|
|
|
|
@retval The linear address.
|
|
**/
|
|
UINTN
|
|
PciSegmentLibVirtualAddress (
|
|
IN UINTN Address
|
|
)
|
|
{
|
|
UINTN Index;
|
|
//
|
|
// If SetVirtualAddressMap() has not been called, then just return the physical address
|
|
//
|
|
if (!EfiGoneVirtual ()) {
|
|
return Address;
|
|
}
|
|
|
|
//
|
|
// See if there is a physical address match at the exact same index as the last address match
|
|
//
|
|
if (mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
|
|
//
|
|
// Convert the physical address to a virtual address and return the virtual address
|
|
//
|
|
return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].VirtualAddress;
|
|
}
|
|
|
|
//
|
|
// Search the entire table for a physical address match
|
|
//
|
|
for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
|
|
if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
|
|
//
|
|
// Cache the matching index value
|
|
//
|
|
mDxeRuntimePciSegmentLibLastRuntimeRange = Index;
|
|
//
|
|
// Convert the physical address to a virtual address and return the virtual address
|
|
//
|
|
return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress;
|
|
}
|
|
}
|
|
|
|
//
|
|
// No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
|
|
//
|
|
ASSERT (FALSE);
|
|
CpuBreakpoint ();
|
|
|
|
//
|
|
// Return the physical address
|
|
//
|
|
return Address;
|
|
}
|