]> git.proxmox.com Git - mirror_edk2.git/blob - MdePkg/Library/PciSegmentLibSegmentInfo/DxeRuntimePciSegmentLib.c
MdePkg: Apply uncrustify changes
[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
208 if (sizeof (UINTN) == sizeof (UINT32)) {
209 ASSERT (EcamAddress < BASE_4GB);
210 }
211
212 Address = (UINTN)EcamAddress;
213
214 //
215 // See if Address has already been registered for runtime access
216 //
217 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
218 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == Address) {
219 return RETURN_SUCCESS;
220 }
221 }
222
223 //
224 // Get the GCD Memory Descriptor for the ECAM Address
225 //
226 Status = gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
227 if (EFI_ERROR (Status)) {
228 return RETURN_UNSUPPORTED;
229 }
230
231 //
232 // Mark the 4KB region for the PCI Express Bus/Dev/Func as EFI_RUNTIME_MEMORY so the OS
233 // will allocate a virtual address range for the 4KB PCI Configuration Header.
234 //
235 Status = gDS->SetMemorySpaceAttributes (Address, EFI_PAGE_SIZE, Descriptor.Attributes | EFI_MEMORY_RUNTIME);
236 if (EFI_ERROR (Status)) {
237 return RETURN_UNSUPPORTED;
238 }
239
240 //
241 // Grow the size of the registration table
242 //
243 NewTable = ReallocateRuntimePool (
244 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 0) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
245 (mDxeRuntimePciSegmentLibNumberOfRuntimeRanges + 1) * sizeof (PCI_SEGMENT_RUNTIME_REGISTRATION_TABLE),
246 mDxeRuntimePciSegmentLibRegistrationTable
247 );
248 if (NewTable == NULL) {
249 return RETURN_OUT_OF_RESOURCES;
250 }
251
252 mDxeRuntimePciSegmentLibRegistrationTable = NewTable;
253 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].PhysicalAddress = Address;
254 mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibNumberOfRuntimeRanges].VirtualAddress = Address;
255 mDxeRuntimePciSegmentLibNumberOfRuntimeRanges++;
256
257 return RETURN_SUCCESS;
258 }
259
260 /**
261 Return the linear address for the physical address.
262
263 @param Address The physical address.
264
265 @retval The linear address.
266 **/
267 UINTN
268 PciSegmentLibVirtualAddress (
269 IN UINTN Address
270 )
271 {
272 UINTN Index;
273
274 //
275 // If SetVirtualAddressMap() has not been called, then just return the physical address
276 //
277 if (!EfiGoneVirtual ()) {
278 return Address;
279 }
280
281 //
282 // See if there is a physical address match at the exact same index as the last address match
283 //
284 if (mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
285 //
286 // Convert the physical address to a virtual address and return the virtual address
287 //
288 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[mDxeRuntimePciSegmentLibLastRuntimeRange].VirtualAddress;
289 }
290
291 //
292 // Search the entire table for a physical address match
293 //
294 for (Index = 0; Index < mDxeRuntimePciSegmentLibNumberOfRuntimeRanges; Index++) {
295 if (mDxeRuntimePciSegmentLibRegistrationTable[Index].PhysicalAddress == (Address & (~(UINTN)EFI_PAGE_MASK))) {
296 //
297 // Cache the matching index value
298 //
299 mDxeRuntimePciSegmentLibLastRuntimeRange = Index;
300 //
301 // Convert the physical address to a virtual address and return the virtual address
302 //
303 return (Address & EFI_PAGE_MASK) + mDxeRuntimePciSegmentLibRegistrationTable[Index].VirtualAddress;
304 }
305 }
306
307 //
308 // No match was found. This is a critical error at OS runtime, so ASSERT() and force a breakpoint.
309 //
310 ASSERT (FALSE);
311 CpuBreakpoint ();
312
313 //
314 // Return the physical address
315 //
316 return Address;
317 }