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