]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/PciSegmentLibSegmentInfo/DxeRuntimePciSegmentLib.c
MdePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdePkg / Library / PciSegmentLibSegmentInfo / DxeRuntimePciSegmentLib.c
1 /** @file
2 Instance of Runtime PCI Segment Library that support multi-segment PCI configuration access.
3
4 PCI Segment Library that consumes segment information provided by PciSegmentInfoLib to
5 support multi-segment PCI configuration access through enhanced configuration access mechanism.
6
7 Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include "PciSegmentLibCommon.h"
13 #include <PiDxe.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>
20
21 ///
22 /// Define table for mapping PCI Segment MMIO physical addresses to virtual addresses at OS runtime
23 ///
24 typedef struct {
25 UINTN PhysicalAddress;
26 UINTN VirtualAddress;
27 } PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE;
28
29 ///
30 /// Set Virtual Address Map Event
31 ///
32 EFI_EVENT mDxeRuntimePciSegmentLibVirtualNotifyEvent = NULL;
33
34 ///
35 /// The number of PCI devices that have been registered for runtime access.
36 ///
37 UINTN mDxeRuntimePciSegmentLibNumberOfRuntimeRanges = 0;
38
39 ///
40 /// The table of PCI devices that have been registered for runtime access.
41 ///
42 PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE *mDxeRuntimePciSegmentLibRegistrationTable = NULL;
43
44 ///
45 /// The table index of the most recent virtual address lookup.
46 ///
47 UINTN mDxeRuntimePciSegmentLibLastRuntimeRange = 0;
48
49 /**
50 Convert the physical PCI Express MMIO addresses for all registered PCI devices
51 to virtual addresses.
52
53 @param[in] Event The event that is being processed.
54 @param[in] Context The Event Context.
55 **/
56 VOID
57 EFIAPI
58 DxeRuntimePciSegmentLibVirtualNotify (
59 IN EFI_EVENT Event,
60 IN VOID *Context
61 )
62 {
63 UINTN Index;
64 EFI_STATUS Status;
65
66 //
67 // If there have been no runtime registrations, then just return
68 //
69 if (mDxeRuntimePciSegmentLibRegistrationTable == NULL) {
70 return;
71 }
72
73 //
74 // Convert physical addresses associated with the set of registered PCI devices to
75 // virtual addresses.
76 //
77 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
78 Status = EfiConvertPointer (0, (VOID **) &(mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress));
79 ASSERT_EFI_ERROR (Status);
80 }
81
82 //
83 // Convert table pointer that is allocated from EfiRuntimeServicesData to a virtual address.
84 //
85 Status = EfiConvertPointer (0, (VOID **) &mDxeRuntimePciSegmentLibRegistrationTable);
86 ASSERT_EFI_ERROR (Status);
87 }
88
89 /**
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.
92
93 @param ImageHandle The firmware allocated handle for the EFI image.
94 @param SystemTable A pointer to the EFI System Table.
95
96 @retval EFI_SUCCESS The constructor completed successfully.
97 @retval Other value The constructor did not complete successfully.
98
99 **/
100 EFI_STATUS
101 EFIAPI
102 DxeRuntimePciSegmentLibConstructor (
103 IN EFI_HANDLE ImageHandle,
104 IN EFI_SYSTEM_TABLE *SystemTable
105 )
106 {
107 EFI_STATUS Status;
108
109 //
110 // Register SetVirtualAddressMap () notify function
111 //
112 Status = gBS->CreateEventEx (
113 EVT_NOTIFY_SIGNAL,
114 TPL_NOTIFY,
115 DxeRuntimePciSegmentLibVirtualNotify,
116 NULL,
117 &gEfiEventVirtualAddressChangeGuid,
118 &mDxeRuntimePciSegmentLibVirtualNotifyEvent
119 );
120 ASSERT_EFI_ERROR (Status);
121
122 return Status;
123 }
124
125 /**
126 The destructor function frees any allocated buffers and closes the Set Virtual
127 Address Map event.
128
129 @param ImageHandle The firmware allocated handle for the EFI image.
130 @param SystemTable A pointer to the EFI System Table.
131
132 @retval EFI_SUCCESS The destructor completed successfully.
133 @retval Other value The destructor did not complete successfully.
134
135 **/
136 EFI_STATUS
137 EFIAPI
138 DxeRuntimePciSegmentLibDestructor (
139 IN EFI_HANDLE ImageHandle,
140 IN EFI_SYSTEM_TABLE *SystemTable
141 )
142 {
143 EFI_STATUS Status;
144
145 //
146 // If one or more PCI devices have been registered for runtime access, then
147 // free the registration table.
148 //
149 if (mDxeRuntimePciSegmentLibRegistrationTable != NULL) {
150 FreePool (mDxeRuntimePciSegmentLibRegistrationTable);
151 }
152
153 //
154 // Close the Set Virtual Address Map event
155 //
156 Status = gBS->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent);
157 ASSERT_EFI_ERROR (Status);
158
159 return Status;
160 }
161
162 /**
163 Register a PCI device so PCI configuration registers may be accessed after
164 SetVirtualAddressMap().
165
166 If any reserved bits in Address are set, then ASSERT().
167
168 @param Address The address that encodes the PCI Bus, Device, Function and
169 Register.
170
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.
178
179 **/
180 RETURN_STATUS
181 EFIAPI
182 PciSegmentRegisterForRuntimeAccess (
183 IN UINTN Address
184 )
185 {
186 RETURN_STATUS Status;
187 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
188 UINTN Index;
189 VOID *NewTable;
190 UINTN Count;
191 PCI_SEGMENT_INFO *SegmentInfo;
192 UINT64 EcamAddress;
193
194 //
195 // Convert Address to a ECAM address at the beginning of the PCI Configuration
196 // header for the specified PCI Bus/Dev/Func
197 //
198 Address &= ~(UINTN)EFI_PAGE_MASK;
199 SegmentInfo = GetPciSegmentInfo (&Count);
200 EcamAddress = PciSegmentLibGetEcamAddress (Address, SegmentInfo, Count);
201
202 //
203 // Return an error if this function is called after ExitBootServices().
204 //
205 if (EfiAtRuntime ()) {
206 return RETURN_UNSUPPORTED;
207 }
208 if (sizeof (UINTN) == sizeof (UINT32)) {
209 ASSERT (EcamAddress < BASE_4GB);
210 }
211 Address = (UINTN)EcamAddress;
212
213 //
214 // See if Address has already been registerd for runtime access
215 //
216 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
217 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == Address) {
218 return RETURN_SUCCESS;
219 }
220 }
221
222 //
223 // Get the GCD Memory Descriptor for the ECAM Address
224 //
225 Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
226 if (EFI_ERROR (Status)) {
227 return RETURN_UNSUPPORTED;
228 }
229
230 //
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.
233 //
234 Status = gDS->SetMemorySpaceAttributes (Address, EFI_PAGE_SIZE, Descriptor.Attributes | EFI_MEMORY_RUNTIME);
235 if (EFI_ERROR (Status)) {
236 return RETURN_UNSUPPORTED;
237 }
238
239 //
240 // Grow the size of the registration table
241 //
242 NewTable = ReallocateRuntimePool (
243 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
244 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
245 mDxeRuntimePciSegmentLibRegistrationTable
246 );
247 if (NewTable == NULL) {
248 return RETURN_OUT_OF_RESOURCES;
249 }
250 mDxeRuntimePciSegmentLibRegistrationTable = NewTable;
251 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].PhysicalAddress = Address;
252 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].VirtualAddress = Address;
253 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges++;
254
255 return RETURN_SUCCESS;
256 }
257
258 /**
259 Return the linear address for the physical address.
260
261 @param Address The physical address.
262
263 @retval The linear address.
264 **/
265 UINTN
266 PciSegmentLibVirtualAddress (
267 IN UINTN Address
268 )
269 {
270 UINTN Index;
271 //
272 // If SetVirtualAddressMap() has not been called, then just return the physical address
273 //
274 if (!EfiGoneVirtual ()) {
275 return Address;
276 }
277
278 //
279 // See if there is a physical address match at the exact same index as the last address match
280 //
281 if (mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
282 //
283 // Convert the physical address to a virtual address and return the virtual address
284 //
285 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].VirtualAddress;
286 }
287
288 //
289 // Search the entire table for a physical address match
290 //
291 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
292 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
293 //
294 // Cache the matching index value
295 //
296 mDxeRuntimePciSegmentLibLastRuntimeRange = Index;
297 //
298 // Convert the physical address to a virtual address and return the virtual address
299 //
300 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress;
301 }
302 }
303
304 //
305 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
306 //
307 ASSERT (FALSE);
308 CpuBreakpoint ();
309
310 //
311 // Return the physical address
312 //
313 return Address;
314 }