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