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