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 This program and the accompanying materials are
9 licensed and made available under the terms and conditions of
10 the BSD License which accompanies this distribution. The full
11 text of the license may be found at
12 http://opensource.org/licenses/bsd-license.php.
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
15 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 #include "PciSegmentLibCommon.h"
21 #include <Guid/EventGroup.h>
22 #include <Library/UefiRuntimeLib.h>
23 #include <Library/DxeServicesTableLib.h>
24 #include <Library/UefiBootServicesTableLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/PciSegmentInfoLib.h>
29 /// Define table for mapping PCI Segment MMIO physical addresses to virtual addresses at OS runtime
32 UINTN PhysicalAddress
;
34 } PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
;
37 /// Set Virtual Address Map Event
39 EFI_EVENT mDxeRuntimePciSegmentLibVirtualNotifyEvent
= NULL
;
42 /// The number of PCI devices that have been registered for runtime access.
44 UINTN mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
= 0;
47 /// The table of PCI devices that have been registered for runtime access.
49 PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
*mDxeRuntimePciSegmentLibRegistrationTable
= NULL
;
52 /// The table index of the most recent virtual address lookup.
54 UINTN mDxeRuntimePciSegmentLibLastRuntimeRange
= 0;
57 Convert the physical PCI Express MMIO addresses for all registered PCI devices
60 @param[in] Event The event that is being processed.
61 @param[in] Context The Event Context.
65 DxeRuntimePciSegmentLibVirtualNotify (
74 // If there have been no runtime registrations, then just return
76 if (mDxeRuntimePciSegmentLibRegistrationTable
== NULL
) {
81 // Convert physical addresses associated with the set of registered PCI devices to
84 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
85 Status
= EfiConvertPointer (0, (VOID
**) &(mDxeRuntimePciSegmentLibRegistrationTable
[Index
].VirtualAddress
));
86 ASSERT_EFI_ERROR (Status
);
90 // Convert table pointer that is allocated from EfiRuntimeServicesData to a virtual address.
92 Status
= EfiConvertPointer (0, (VOID
**) &mDxeRuntimePciSegmentLibRegistrationTable
);
93 ASSERT_EFI_ERROR (Status
);
97 The constructor function caches the PCI Express Base Address and creates a
98 Set Virtual Address Map event to convert physical address to virtual addresses.
100 @param ImageHandle The firmware allocated handle for the EFI image.
101 @param SystemTable A pointer to the EFI System Table.
103 @retval EFI_SUCCESS The constructor completed successfully.
104 @retval Other value The constructor did not complete successfully.
109 DxeRuntimePciSegmentLibConstructor (
110 IN EFI_HANDLE ImageHandle
,
111 IN EFI_SYSTEM_TABLE
*SystemTable
117 // Register SetVirtualAddressMap () notify function
119 Status
= gBS
->CreateEventEx (
122 DxeRuntimePciSegmentLibVirtualNotify
,
124 &gEfiEventVirtualAddressChangeGuid
,
125 &mDxeRuntimePciSegmentLibVirtualNotifyEvent
127 ASSERT_EFI_ERROR (Status
);
133 The destructor function frees any allocated buffers and closes the Set Virtual
136 @param ImageHandle The firmware allocated handle for the EFI image.
137 @param SystemTable A pointer to the EFI System Table.
139 @retval EFI_SUCCESS The destructor completed successfully.
140 @retval Other value The destructor did not complete successfully.
145 DxeRuntimePciSegmentLibDestructor (
146 IN EFI_HANDLE ImageHandle
,
147 IN EFI_SYSTEM_TABLE
*SystemTable
153 // If one or more PCI devices have been registered for runtime access, then
154 // free the registration table.
156 if (mDxeRuntimePciSegmentLibRegistrationTable
!= NULL
) {
157 FreePool (mDxeRuntimePciSegmentLibRegistrationTable
);
161 // Close the Set Virtual Address Map event
163 Status
= gBS
->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent
);
164 ASSERT_EFI_ERROR (Status
);
170 Register a PCI device so PCI configuration registers may be accessed after
171 SetVirtualAddressMap().
173 If any reserved bits in Address are set, then ASSERT().
175 @param Address The address that encodes the PCI Bus, Device, Function and
178 @retval RETURN_SUCCESS The PCI device was registered for runtime access.
179 @retval RETURN_UNSUPPORTED An attempt was made to call this function
180 after ExitBootServices().
181 @retval RETURN_UNSUPPORTED The resources required to access the PCI device
182 at runtime could not be mapped.
183 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
184 complete the registration.
189 PciSegmentRegisterForRuntimeAccess (
193 RETURN_STATUS Status
;
194 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
198 PCI_SEGMENT_INFO
*SegmentInfo
;
202 // Convert Address to a ECAM address at the beginning of the PCI Configuration
203 // header for the specified PCI Bus/Dev/Func
205 Address
&= ~(UINTN
)EFI_PAGE_MASK
;
206 SegmentInfo
= GetPciSegmentInfo (&Count
);
207 EcamAddress
= PciSegmentLibGetEcamAddress (Address
, SegmentInfo
, Count
);
210 // Return an error if this function is called after ExitBootServices().
212 if (EfiAtRuntime ()) {
213 return RETURN_UNSUPPORTED
;
215 if (sizeof (UINTN
) == sizeof (UINT32
)) {
216 ASSERT (EcamAddress
< BASE_4GB
);
218 Address
= (UINTN
)EcamAddress
;
221 // See if Address has already been registerd for runtime access
223 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
224 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== Address
) {
225 return RETURN_SUCCESS
;
230 // Get the GCD Memory Descriptor for the ECAM Address
232 Status
= gDS
->GetMemorySpaceDescriptor (Address
, &Descriptor
);
233 if (EFI_ERROR (Status
)) {
234 return RETURN_UNSUPPORTED
;
238 // Mark the 4KB region for the PCI Express Bus/Dev/Func as EFI_RUNTIME_MEMORY so the OS
239 // will allocate a virtual address range for the 4KB PCI Configuration Header.
241 Status
= gDS
->SetMemorySpaceAttributes (Address
, EFI_PAGE_SIZE
, Descriptor
.Attributes
| EFI_MEMORY_RUNTIME
);
242 if (EFI_ERROR (Status
)) {
243 return RETURN_UNSUPPORTED
;
247 // Grow the size of the registration table
249 NewTable
= ReallocateRuntimePool (
250 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
251 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
252 mDxeRuntimePciSegmentLibRegistrationTable
254 if (NewTable
== NULL
) {
255 return RETURN_OUT_OF_RESOURCES
;
257 mDxeRuntimePciSegmentLibRegistrationTable
= NewTable
;
258 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].PhysicalAddress
= Address
;
259 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].VirtualAddress
= Address
;
260 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
++;
262 return RETURN_SUCCESS
;
266 Return the linear address for the physical address.
268 @param Address The physical address.
270 @retval The linear address.
273 PciSegmentLibVirtualAddress (
279 // If SetVirtualAddressMap() has not been called, then just return the physical address
281 if (!EfiGoneVirtual ()) {
286 // See if there is a physical address match at the exact same index as the last address match
288 if (mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
290 // Convert the physical address to a virtual address and return the virtual address
292 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].VirtualAddress
;
296 // Search the entire table for a physical address match
298 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
299 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
301 // Cache the matching index value
303 mDxeRuntimePciSegmentLibLastRuntimeRange
= Index
;
305 // Convert the physical address to a virtual address and return the virtual address
307 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[Index
].VirtualAddress
;
312 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
318 // Return the physical address