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