2 Instance of Runtime PCI Segment Library that support multi-segment PCI configuration access.
4 PCI Segment Library that consumes segment information provided by PciSegmentInfoLib to
5 support multi-segment PCI configuration access through enhanced configuration access mechanism.
7 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include "PciSegmentLibCommon.h"
14 #include <Guid/EventGroup.h>
15 #include <Library/UefiRuntimeLib.h>
16 #include <Library/DxeServicesTableLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/MemoryAllocationLib.h>
19 #include <Library/PciSegmentInfoLib.h>
22 /// Define table for mapping PCI Segment MMIO physical addresses to virtual addresses at OS runtime
25 UINTN PhysicalAddress
;
27 } PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
;
30 /// Set Virtual Address Map Event
32 EFI_EVENT mDxeRuntimePciSegmentLibVirtualNotifyEvent
= NULL
;
35 /// The number of PCI devices that have been registered for runtime access.
37 UINTN mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
= 0;
40 /// The table of PCI devices that have been registered for runtime access.
42 PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
*mDxeRuntimePciSegmentLibRegistrationTable
= NULL
;
45 /// The table index of the most recent virtual address lookup.
47 UINTN mDxeRuntimePciSegmentLibLastRuntimeRange
= 0;
50 Convert the physical PCI Express MMIO addresses for all registered PCI devices
53 @param[in] Event The event that is being processed.
54 @param[in] Context The Event Context.
58 DxeRuntimePciSegmentLibVirtualNotify (
67 // If there have been no runtime registrations, then just return
69 if (mDxeRuntimePciSegmentLibRegistrationTable
== NULL
) {
74 // Convert physical addresses associated with the set of registered PCI devices to
77 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
78 Status
= EfiConvertPointer (0, (VOID
**) &(mDxeRuntimePciSegmentLibRegistrationTable
[Index
].VirtualAddress
));
79 ASSERT_EFI_ERROR (Status
);
83 // Convert table pointer that is allocated from EfiRuntimeServicesData to a virtual address.
85 Status
= EfiConvertPointer (0, (VOID
**) &mDxeRuntimePciSegmentLibRegistrationTable
);
86 ASSERT_EFI_ERROR (Status
);
90 The constructor function caches the PCI Express Base Address and creates a
91 Set Virtual Address Map event to convert physical address to virtual addresses.
93 @param ImageHandle The firmware allocated handle for the EFI image.
94 @param SystemTable A pointer to the EFI System Table.
96 @retval EFI_SUCCESS The constructor completed successfully.
97 @retval Other value The constructor did not complete successfully.
102 DxeRuntimePciSegmentLibConstructor (
103 IN EFI_HANDLE ImageHandle
,
104 IN EFI_SYSTEM_TABLE
*SystemTable
110 // Register SetVirtualAddressMap () notify function
112 Status
= gBS
->CreateEventEx (
115 DxeRuntimePciSegmentLibVirtualNotify
,
117 &gEfiEventVirtualAddressChangeGuid
,
118 &mDxeRuntimePciSegmentLibVirtualNotifyEvent
120 ASSERT_EFI_ERROR (Status
);
126 The destructor function frees any allocated buffers and closes the Set Virtual
129 @param ImageHandle The firmware allocated handle for the EFI image.
130 @param SystemTable A pointer to the EFI System Table.
132 @retval EFI_SUCCESS The destructor completed successfully.
133 @retval Other value The destructor did not complete successfully.
138 DxeRuntimePciSegmentLibDestructor (
139 IN EFI_HANDLE ImageHandle
,
140 IN EFI_SYSTEM_TABLE
*SystemTable
146 // If one or more PCI devices have been registered for runtime access, then
147 // free the registration table.
149 if (mDxeRuntimePciSegmentLibRegistrationTable
!= NULL
) {
150 FreePool (mDxeRuntimePciSegmentLibRegistrationTable
);
154 // Close the Set Virtual Address Map event
156 Status
= gBS
->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent
);
157 ASSERT_EFI_ERROR (Status
);
163 Register a PCI device so PCI configuration registers may be accessed after
164 SetVirtualAddressMap().
166 If any reserved bits in Address are set, then ASSERT().
168 @param Address The address that encodes the PCI Bus, Device, Function and
171 @retval RETURN_SUCCESS The PCI device was registered for runtime access.
172 @retval RETURN_UNSUPPORTED An attempt was made to call this function
173 after ExitBootServices().
174 @retval RETURN_UNSUPPORTED The resources required to access the PCI device
175 at runtime could not be mapped.
176 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
177 complete the registration.
182 PciSegmentRegisterForRuntimeAccess (
186 RETURN_STATUS Status
;
187 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
191 PCI_SEGMENT_INFO
*SegmentInfo
;
195 // Convert Address to a ECAM address at the beginning of the PCI Configuration
196 // header for the specified PCI Bus/Dev/Func
198 Address
&= ~(UINTN
)EFI_PAGE_MASK
;
199 SegmentInfo
= GetPciSegmentInfo (&Count
);
200 EcamAddress
= PciSegmentLibGetEcamAddress (Address
, SegmentInfo
, Count
);
203 // Return an error if this function is called after ExitBootServices().
205 if (EfiAtRuntime ()) {
206 return RETURN_UNSUPPORTED
;
208 if (sizeof (UINTN
) == sizeof (UINT32
)) {
209 ASSERT (EcamAddress
< BASE_4GB
);
211 Address
= (UINTN
)EcamAddress
;
214 // See if Address has already been registerd for runtime access
216 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
217 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== Address
) {
218 return RETURN_SUCCESS
;
223 // Get the GCD Memory Descriptor for the ECAM Address
225 Status
= gDS
->GetMemorySpaceDescriptor (Address
, &Descriptor
);
226 if (EFI_ERROR (Status
)) {
227 return RETURN_UNSUPPORTED
;
231 // Mark the 4KB region for the PCI Express Bus/Dev/Func as EFI_RUNTIME_MEMORY so the OS
232 // will allocate a virtual address range for the 4KB PCI Configuration Header.
234 Status
= gDS
->SetMemorySpaceAttributes (Address
, EFI_PAGE_SIZE
, Descriptor
.Attributes
| EFI_MEMORY_RUNTIME
);
235 if (EFI_ERROR (Status
)) {
236 return RETURN_UNSUPPORTED
;
240 // Grow the size of the registration table
242 NewTable
= ReallocateRuntimePool (
243 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
244 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
245 mDxeRuntimePciSegmentLibRegistrationTable
247 if (NewTable
== NULL
) {
248 return RETURN_OUT_OF_RESOURCES
;
250 mDxeRuntimePciSegmentLibRegistrationTable
= NewTable
;
251 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].PhysicalAddress
= Address
;
252 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].VirtualAddress
= Address
;
253 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
++;
255 return RETURN_SUCCESS
;
259 Return the linear address for the physical address.
261 @param Address The physical address.
263 @retval The linear address.
266 PciSegmentLibVirtualAddress (
272 // If SetVirtualAddressMap() has not been called, then just return the physical address
274 if (!EfiGoneVirtual ()) {
279 // See if there is a physical address match at the exact same index as the last address match
281 if (mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
283 // Convert the physical address to a virtual address and return the virtual address
285 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].VirtualAddress
;
289 // Search the entire table for a physical address match
291 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
292 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
294 // Cache the matching index value
296 mDxeRuntimePciSegmentLibLastRuntimeRange
= Index
;
298 // Convert the physical address to a virtual address and return the virtual address
300 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[Index
].VirtualAddress
;
305 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
311 // Return the physical address