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