]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/PciSegmentLibSegmentInfo/DxeRuntimePciSegmentLib.c
b0dbdec0cf85590e2e6c6af96a0e505ce8dbca55
[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->CreateEvent (
113 EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
114 TPL_NOTIFY,
115 DxeRuntimePciSegmentLibVirtualNotify,
116 NULL,
117 &mDxeRuntimePciSegmentLibVirtualNotifyEvent
118 );
119 ASSERT_EFI_ERROR (Status);
120
121 return Status;
122 }
123
124 /**
125 The destructor function frees any allocated buffers and closes the Set Virtual
126 Address Map event.
127
128 @param ImageHandle The firmware allocated handle for the EFI image.
129 @param SystemTable A pointer to the EFI System Table.
130
131 @retval EFI_SUCCESS The destructor completed successfully.
132 @retval Other value The destructor did not complete successfully.
133
134 **/
135 EFI_STATUS
136 EFIAPI
137 DxeRuntimePciSegmentLibDestructor (
138 IN EFI_HANDLE ImageHandle,
139 IN EFI_SYSTEM_TABLE *SystemTable
140 )
141 {
142 EFI_STATUS Status;
143
144 //
145 // If one or more PCI devices have been registered for runtime access, then
146 // free the registration table.
147 //
148 if (mDxeRuntimePciSegmentLibRegistrationTable != NULL) {
149 FreePool (mDxeRuntimePciSegmentLibRegistrationTable);
150 }
151
152 //
153 // Close the Set Virtual Address Map event
154 //
155 Status = gBS->CloseEvent (mDxeRuntimePciSegmentLibVirtualNotifyEvent);
156 ASSERT_EFI_ERROR (Status);
157
158 return Status;
159 }
160
161 /**
162 Register a PCI device so PCI configuration registers may be accessed after
163 SetVirtualAddressMap().
164
165 If any reserved bits in Address are set, then ASSERT().
166
167 @param Address The address that encodes the PCI Bus, Device, Function and
168 Register.
169
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.
177
178 **/
179 RETURN_STATUS
180 EFIAPI
181 PciSegmentRegisterForRuntimeAccess (
182 IN UINTN Address
183 )
184 {
185 RETURN_STATUS Status;
186 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
187 UINTN Index;
188 VOID *NewTable;
189 UINTN Count;
190 PCI_SEGMENT_INFO *SegmentInfo;
191 UINT64 EcamAddress;
192
193 //
194 // Convert Address to a ECAM address at the beginning of the PCI Configuration
195 // header for the specified PCI Bus/Dev/Func
196 //
197 Address &= ~(UINTN)EFI_PAGE_MASK;
198 SegmentInfo = GetPciSegmentInfo (&Count);
199 EcamAddress = PciSegmentLibGetEcamAddress (Address, SegmentInfo, Count);
200
201 //
202 // Return an error if this function is called after ExitBootServices().
203 //
204 if (EfiAtRuntime ()) {
205 return RETURN_UNSUPPORTED;
206 }
207 if (sizeof (UINTN) == sizeof (UINT32)) {
208 ASSERT (EcamAddress < BASE_4GB);
209 }
210 Address = (UINTN)EcamAddress;
211
212 //
213 // See if Address has already been registerd for runtime access
214 //
215 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
216 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == Address) {
217 return RETURN_SUCCESS;
218 }
219 }
220
221 //
222 // Get the GCD Memory Descriptor for the ECAM Address
223 //
224 Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
225 if (EFI_ERROR (Status)) {
226 return RETURN_UNSUPPORTED;
227 }
228
229 //
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.
232 //
233 Status = gDS->SetMemorySpaceAttributes (Address, EFI_PAGE_SIZE, Descriptor.Attributes | EFI_MEMORY_RUNTIME);
234 if (EFI_ERROR (Status)) {
235 return RETURN_UNSUPPORTED;
236 }
237
238 //
239 // Grow the size of the registration table
240 //
241 NewTable = ReallocateRuntimePool (
242 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
243 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
244 mDxeRuntimePciSegmentLibRegistrationTable
245 );
246 if (NewTable == NULL) {
247 return RETURN_OUT_OF_RESOURCES;
248 }
249 mDxeRuntimePciSegmentLibRegistrationTable = NewTable;
250 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].PhysicalAddress = Address;
251 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].VirtualAddress = Address;
252 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges++;
253
254 return RETURN_SUCCESS;
255 }
256
257 /**
258 Return the linear address for the physical address.
259
260 @param Address The physical address.
261
262 @retval The linear address.
263 **/
264 UINTN
265 PciSegmentLibVirtualAddress (
266 IN UINTN Address
267 )
268 {
269 UINTN Index;
270 //
271 // If SetVirtualAddressMap() has not been called, then just return the physical address
272 //
273 if (!EfiGoneVirtual ()) {
274 return Address;
275 }
276
277 //
278 // See if there is a physical address match at the exact same index as the last address match
279 //
280 if (mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
281 //
282 // Convert the physical address to a virtual address and return the virtual address
283 //
284 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].VirtualAddress;
285 }
286
287 //
288 // Search the entire table for a physical address match
289 //
290 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
291 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
292 //
293 // Cache the matching index value
294 //
295 mDxeRuntimePciSegmentLibLastRuntimeRange = Index;
296 //
297 // Convert the physical address to a virtual address and return the virtual address
298 //
299 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress;
300 }
301 }
302
303 //
304 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
305 //
306 ASSERT (FALSE);
307 CpuBreakpoint ();
308
309 //
310 // Return the physical address
311 //
312 return Address;
313 }