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
->CreateEvent (
113 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE
,
115 DxeRuntimePciSegmentLibVirtualNotify
,
117 &mDxeRuntimePciSegmentLibVirtualNotifyEvent
119 ASSERT_EFI_ERROR (Status
);
125 The destructor function frees any allocated buffers and closes the Set Virtual
128 @param ImageHandle The firmware allocated handle for the EFI image.
129 @param SystemTable A pointer to the EFI System Table.
131 @retval EFI_SUCCESS The destructor completed successfully.
132 @retval Other value The destructor did not complete successfully.
137 DxeRuntimePciSegmentLibDestructor (
138 IN EFI_HANDLE ImageHandle
,
139 IN EFI_SYSTEM_TABLE
*SystemTable
145 // If one or more PCI devices have been registered for runtime access, then
146 // free the registration table.
148 if (mDxeRuntimePciSegmentLibRegistrationTable
!= NULL
) {
149 FreePool (mDxeRuntimePciSegmentLibRegistrationTable
);
153 // Close the Set Virtual Address Map event
155 Status
= gBS
->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent
);
156 ASSERT_EFI_ERROR (Status
);
162 Register a PCI device so PCI configuration registers may be accessed after
163 SetVirtualAddressMap().
165 If any reserved bits in Address are set, then ASSERT().
167 @param Address The address that encodes the PCI Bus, Device, Function and
170 @retval RETURN_SUCCESS The PCI device was registered for runtime access.
171 @retval RETURN_UNSUPPORTED An attempt was made to call this function
172 after ExitBootServices().
173 @retval RETURN_UNSUPPORTED The resources required to access the PCI device
174 at runtime could not be mapped.
175 @retval RETURN_OUT_OF_RESOURCES There are not enough resources available to
176 complete the registration.
181 PciSegmentRegisterForRuntimeAccess (
185 RETURN_STATUS Status
;
186 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor
;
190 PCI_SEGMENT_INFO
*SegmentInfo
;
194 // Convert Address to a ECAM address at the beginning of the PCI Configuration
195 // header for the specified PCI Bus/Dev/Func
197 Address
&= ~(UINTN
)EFI_PAGE_MASK
;
198 SegmentInfo
= GetPciSegmentInfo (&Count
);
199 EcamAddress
= PciSegmentLibGetEcamAddress (Address
, SegmentInfo
, Count
);
202 // Return an error if this function is called after ExitBootServices().
204 if (EfiAtRuntime ()) {
205 return RETURN_UNSUPPORTED
;
207 if (sizeof (UINTN
) == sizeof (UINT32
)) {
208 ASSERT (EcamAddress
< BASE_4GB
);
210 Address
= (UINTN
)EcamAddress
;
213 // See if Address has already been registerd for runtime access
215 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
216 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== Address
) {
217 return RETURN_SUCCESS
;
222 // Get the GCD Memory Descriptor for the ECAM Address
224 Status
= gDS
->GetMemorySpaceDescriptor (Address
, &Descriptor
);
225 if (EFI_ERROR (Status
)) {
226 return RETURN_UNSUPPORTED
;
230 // Mark the 4KB region for the PCI Express Bus/Dev/Func as EFI_RUNTIME_MEMORY so the OS
231 // will allocate a virtual address range for the 4KB PCI Configuration Header.
233 Status
= gDS
->SetMemorySpaceAttributes (Address
, EFI_PAGE_SIZE
, Descriptor
.Attributes
| EFI_MEMORY_RUNTIME
);
234 if (EFI_ERROR (Status
)) {
235 return RETURN_UNSUPPORTED
;
239 // Grow the size of the registration table
241 NewTable
= ReallocateRuntimePool (
242 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
243 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
+ 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE
),
244 mDxeRuntimePciSegmentLibRegistrationTable
246 if (NewTable
== NULL
) {
247 return RETURN_OUT_OF_RESOURCES
;
249 mDxeRuntimePciSegmentLibRegistrationTable
= NewTable
;
250 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].PhysicalAddress
= Address
;
251 mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
].VirtualAddress
= Address
;
252 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
++;
254 return RETURN_SUCCESS
;
258 Return the linear address for the physical address.
260 @param Address The physical address.
262 @retval The linear address.
265 PciSegmentLibVirtualAddress (
271 // If SetVirtualAddressMap() has not been called, then just return the physical address
273 if (!EfiGoneVirtual ()) {
278 // See if there is a physical address match at the exact same index as the last address match
280 if (mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
282 // Convert the physical address to a virtual address and return the virtual address
284 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[mDxeRuntimePciSegmentLibLastRuntimeRange
].VirtualAddress
;
288 // Search the entire table for a physical address match
290 for (Index
= 0; Index
< mDxeRuntimePciSegmentLibNumberOfRuntimeRanges
; Index
++) {
291 if (mDxeRuntimePciSegmentLibRegistrationTable
[Index
].PhysicalAddress
== (Address
& (~(UINTN
)EFI_PAGE_MASK
))) {
293 // Cache the matching index value
295 mDxeRuntimePciSegmentLibLastRuntimeRange
= Index
;
297 // Convert the physical address to a virtual address and return the virtual address
299 return (Address
& EFI_PAGE_MASK
) + mDxeRuntimePciSegmentLibRegistrationTable
[Index
].VirtualAddress
;
304 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
310 // Return the physical address