MdePkg/PciSegmentLib: Add instances that consumes PciSegmentInfoLib
The patch adds two PciSegmentLib instances that consumes PciSegmentInfoLib to provide multiple segments PCI configuration access. BasePciSegmentLibSegmentInfo instance is a BASE library. DxeRuntimePciSegmentLibSegmentInfo instance is to be linked with runtime drivers to provide not only boot time but also runtime PCI configuration access. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com> Reviewed-by: Liming Gao <liming.gao@intel.com>
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
/** @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;
|
||||
}
|
Reference in New Issue
Block a user