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