]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/PciBusDxe/PciHotPlugSupport.c
MdeModulePkg/PciBusDxe: catch unimplemented extended config space reads
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciBusDxe / PciHotPlugSupport.c
CommitLineData
9060e3ec 1/** @file\r
2 PCI Hot Plug support functions implementation for PCI Bus module..\r
3\r
fcdfcdbf 4Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
9d510e61 5SPDX-License-Identifier: BSD-2-Clause-Patent\r
9060e3ec 6\r
7**/\r
8\r
9#include "PciBus.h"\r
10\r
11EFI_PCI_HOT_PLUG_INIT_PROTOCOL *gPciHotPlugInit = NULL;\r
12EFI_HPC_LOCATION *gPciRootHpcPool = NULL;\r
13UINTN gPciRootHpcCount = 0;\r
14ROOT_HPC_DATA *gPciRootHpcData = NULL;\r
15\r
16\r
17/**\r
18 Event notification function to set Hot Plug controller status.\r
19\r
20 @param Event The event that invoke this function.\r
21 @param Context The calling context, pointer to ROOT_HPC_DATA.\r
22\r
23**/\r
24VOID\r
25EFIAPI\r
26PciHPCInitialized (\r
27 IN EFI_EVENT Event,\r
28 IN VOID *Context\r
29 )\r
30{\r
31 ROOT_HPC_DATA *HpcData;\r
32\r
33 HpcData = (ROOT_HPC_DATA *) Context;\r
34 HpcData->Initialized = TRUE;\r
35}\r
36\r
37/**\r
fcdfcdbf 38 Compare two device paths to check if they are exactly same.\r
9060e3ec 39\r
40 @param DevicePath1 A pointer to the first device path data structure.\r
41 @param DevicePath2 A pointer to the second device path data structure.\r
42\r
43 @retval TRUE They are same.\r
44 @retval FALSE They are not same.\r
45\r
46**/\r
47BOOLEAN\r
48EfiCompareDevicePath (\r
49 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1,\r
50 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2\r
51 )\r
52{\r
53 UINTN Size1;\r
54 UINTN Size2;\r
55\r
56 Size1 = GetDevicePathSize (DevicePath1);\r
57 Size2 = GetDevicePathSize (DevicePath2);\r
58\r
59 if (Size1 != Size2) {\r
60 return FALSE;\r
61 }\r
62\r
63 if (CompareMem (DevicePath1, DevicePath2, Size1) != 0) {\r
64 return FALSE;\r
65 }\r
66\r
67 return TRUE;\r
68}\r
69\r
70/**\r
71 Check hot plug support and initialize root hot plug private data.\r
72\r
73 If Hot Plug is supported by the platform, call PCI Hot Plug Init protocol\r
74 to get PCI Hot Plug controller's information and constructor the root hot plug\r
75 private data structure.\r
76\r
77 @retval EFI_SUCCESS They are same.\r
fcdfcdbf 78 @retval EFI_UNSUPPORTED No PCI Hot Plug controller on the platform.\r
9060e3ec 79 @retval EFI_OUT_OF_RESOURCES No memory to constructor root hot plug private\r
80 data structure.\r
81\r
82**/\r
83EFI_STATUS\r
84InitializeHotPlugSupport (\r
85 VOID\r
86 )\r
87{\r
88 EFI_STATUS Status;\r
89 EFI_HPC_LOCATION *HpcList;\r
90 UINTN HpcCount;\r
91\r
92 //\r
93 // Locate the PciHotPlugInit Protocol\r
94 // If it doesn't exist, that means there is no\r
95 // hot plug controller supported on the platform\r
96 // the PCI Bus driver is running on. HotPlug Support\r
97 // is an optional feature, so absence of the protocol\r
98 // won't incur the penalty.\r
99 //\r
100 Status = gBS->LocateProtocol (\r
101 &gEfiPciHotPlugInitProtocolGuid,\r
102 NULL,\r
103 (VOID **) &gPciHotPlugInit\r
104 );\r
105\r
106 if (EFI_ERROR (Status)) {\r
107 return EFI_UNSUPPORTED;\r
108 }\r
109\r
110 Status = gPciHotPlugInit->GetRootHpcList (\r
111 gPciHotPlugInit,\r
112 &HpcCount,\r
113 &HpcList\r
114 );\r
115\r
116 if (!EFI_ERROR (Status)) {\r
117\r
118 gPciRootHpcPool = HpcList;\r
119 gPciRootHpcCount = HpcCount;\r
120 gPciRootHpcData = AllocateZeroPool (sizeof (ROOT_HPC_DATA) * gPciRootHpcCount);\r
121 if (gPciRootHpcData == NULL) {\r
122 return EFI_OUT_OF_RESOURCES;\r
123 }\r
124 }\r
125\r
126 return EFI_SUCCESS;\r
127}\r
128\r
129/**\r
130 Test whether device path is for root pci hot plug bus.\r
131\r
132 @param HpbDevicePath A pointer to device path data structure to be tested.\r
133 @param HpIndex If HpIndex is not NULL, return the index of root hot\r
fcdfcdbf 134 plug in global array when TRUE is returned.\r
9060e3ec 135\r
136 @retval TRUE The device path is for root pci hot plug bus.\r
137 @retval FALSE The device path is not for root pci hot plug bus.\r
138\r
139**/\r
140BOOLEAN\r
141IsRootPciHotPlugBus (\r
142 IN EFI_DEVICE_PATH_PROTOCOL *HpbDevicePath,\r
143 OUT UINTN *HpIndex OPTIONAL\r
144 )\r
145{\r
146 UINTN Index;\r
147\r
148 for (Index = 0; Index < gPciRootHpcCount; Index++) {\r
149\r
150 if (EfiCompareDevicePath (gPciRootHpcPool[Index].HpbDevicePath, HpbDevicePath)) {\r
151\r
152 if (HpIndex != NULL) {\r
153 *HpIndex = Index;\r
154 }\r
155\r
156 return TRUE;\r
157 }\r
158 }\r
159\r
160 return FALSE;\r
161}\r
162\r
163/**\r
164 Test whether device path is for root pci hot plug controller.\r
165\r
166 @param HpcDevicePath A pointer to device path data structure to be tested.\r
167 @param HpIndex If HpIndex is not NULL, return the index of root hot\r
fcdfcdbf 168 plug in global array when TRUE is returned.\r
9060e3ec 169\r
170 @retval TRUE The device path is for root pci hot plug controller.\r
171 @retval FALSE The device path is not for root pci hot plug controller.\r
172\r
173**/\r
174BOOLEAN\r
175IsRootPciHotPlugController (\r
176 IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath,\r
177 OUT UINTN *HpIndex\r
178 )\r
179{\r
180 UINTN Index;\r
181\r
182 for (Index = 0; Index < gPciRootHpcCount; Index++) {\r
183\r
184 if (EfiCompareDevicePath (gPciRootHpcPool[Index].HpcDevicePath, HpcDevicePath)) {\r
185\r
186 if (HpIndex != NULL) {\r
187 *HpIndex = Index;\r
188 }\r
189\r
190 return TRUE;\r
191 }\r
192 }\r
193\r
194 return FALSE;\r
195}\r
196\r
197/**\r
198 Creating event object for PCI Hot Plug controller.\r
199\r
200 @param HpIndex Index of hot plug device in global array.\r
fcdfcdbf 201 @param Event The returned event that invoke this function.\r
9060e3ec 202\r
fcdfcdbf 203 @return Status of create event.\r
9060e3ec 204\r
205**/\r
206EFI_STATUS\r
207CreateEventForHpc (\r
208 IN UINTN HpIndex,\r
209 OUT EFI_EVENT *Event\r
210 )\r
211{\r
212 EFI_STATUS Status;\r
213\r
214 Status = gBS->CreateEvent (\r
215 EVT_NOTIFY_SIGNAL,\r
216 TPL_CALLBACK,\r
217 PciHPCInitialized,\r
218 gPciRootHpcData + HpIndex,\r
219 &((gPciRootHpcData + HpIndex)->Event)\r
220 );\r
221\r
222 if (!EFI_ERROR (Status)) {\r
223 *Event = (gPciRootHpcData + HpIndex)->Event;\r
224 }\r
225\r
226 return Status;\r
227}\r
228\r
229/**\r
230 Wait for all root PCI Hot Plug controller finished initializing.\r
231\r
232 @param TimeoutInMicroSeconds Microseconds to wait for all root HPCs' initialization.\r
233\r
234 @retval EFI_SUCCESS All HPCs initialization finished.\r
235 @retval EFI_TIMEOUT Not ALL HPCs initialization finished in Microseconds.\r
236\r
237**/\r
238EFI_STATUS\r
239AllRootHPCInitialized (\r
240 IN UINTN TimeoutInMicroSeconds\r
241 )\r
242{\r
243 UINT32 Delay;\r
244 UINTN Index;\r
245\r
246 Delay = (UINT32) ((TimeoutInMicroSeconds / 30) + 1);\r
247\r
248 do {\r
249 for (Index = 0; Index < gPciRootHpcCount; Index++) {\r
250\r
55565b08 251 if (gPciRootHpcData[Index].Found && !gPciRootHpcData[Index].Initialized) {\r
9060e3ec 252 break;\r
253 }\r
254 }\r
255\r
256 if (Index == gPciRootHpcCount) {\r
257 return EFI_SUCCESS;\r
258 }\r
259\r
260 //\r
261 // Stall for 30 microseconds..\r
262 //\r
263 gBS->Stall (30);\r
264\r
265 Delay--;\r
266\r
267 } while (Delay > 0);\r
268\r
269 return EFI_TIMEOUT;\r
270}\r
271\r
272/**\r
273 Check whether PCI-PCI bridge has PCI Hot Plug capability register block.\r
274\r
275 @param PciIoDevice A Pointer to the PCI-PCI bridge.\r
276\r
277 @retval TRUE PCI device is HPC.\r
278 @retval FALSE PCI device is not HPC.\r
279\r
280**/\r
281BOOLEAN\r
282IsSHPC (\r
283 IN PCI_IO_DEVICE *PciIoDevice\r
284 )\r
285{\r
286\r
287 EFI_STATUS Status;\r
288 UINT8 Offset;\r
289\r
290 if (PciIoDevice == NULL) {\r
291 return FALSE;\r
292 }\r
293\r
294 Offset = 0;\r
295 Status = LocateCapabilityRegBlock (\r
296 PciIoDevice,\r
c6b5fb73 297 EFI_PCI_CAPABILITY_ID_SHPC,\r
9060e3ec 298 &Offset,\r
299 NULL\r
300 );\r
301\r
302 //\r
303 // If the PCI-PCI bridge has the hot plug controller build-in,\r
304 // then return TRUE;\r
305 //\r
306 if (!EFI_ERROR (Status)) {\r
307 return TRUE;\r
308 }\r
309\r
310 return FALSE;\r
311}\r
312\r
ffdd3376
LE
313/**\r
314 Check whether PciIoDevice supports PCIe hotplug.\r
315\r
316 This is equivalent to the following condition:\r
317 - the device is either a PCIe switch downstream port or a root port,\r
318 - and the device has the SlotImplemented bit set in its PCIe capability\r
319 register,\r
320 - and the device has the HotPlugCapable bit set in its slot capabilities\r
321 register.\r
322\r
323 @param[in] PciIoDevice The device being checked.\r
324\r
fcdfcdbf 325 @retval TRUE PciIoDevice is a PCIe port that accepts a hot-plugged device.\r
ffdd3376
LE
326 @retval FALSE Otherwise.\r
327\r
328**/\r
329BOOLEAN\r
330SupportsPcieHotplug (\r
331 IN PCI_IO_DEVICE *PciIoDevice\r
332 )\r
333{\r
334 UINT32 Offset;\r
335 EFI_STATUS Status;\r
336 PCI_REG_PCIE_CAPABILITY Capability;\r
337 PCI_REG_PCIE_SLOT_CAPABILITY SlotCapability;\r
338\r
339 if (PciIoDevice == NULL) {\r
340 return FALSE;\r
341 }\r
342\r
343 //\r
344 // Read the PCI Express Capabilities Register\r
345 //\r
346 if (!PciIoDevice->IsPciExp) {\r
347 return FALSE;\r
348 }\r
349 Offset = PciIoDevice->PciExpressCapabilityOffset +\r
350 OFFSET_OF (PCI_CAPABILITY_PCIEXP, Capability);\r
351 Status = PciIoDevice->PciIo.Pci.Read (\r
352 &PciIoDevice->PciIo,\r
353 EfiPciIoWidthUint16,\r
354 Offset,\r
355 1,\r
356 &Capability\r
357 );\r
358 if (EFI_ERROR (Status)) {\r
359 return FALSE;\r
360 }\r
361\r
362 //\r
363 // Check the contents of the register\r
364 //\r
365 switch (Capability.Bits.DevicePortType) {\r
366 case PCIE_DEVICE_PORT_TYPE_ROOT_PORT:\r
367 case PCIE_DEVICE_PORT_TYPE_DOWNSTREAM_PORT:\r
368 break;\r
369 default:\r
370 return FALSE;\r
371 }\r
372 if (!Capability.Bits.SlotImplemented) {\r
373 return FALSE;\r
374 }\r
375\r
376 //\r
377 // Read the Slot Capabilities Register\r
378 //\r
379 Offset = PciIoDevice->PciExpressCapabilityOffset +\r
380 OFFSET_OF (PCI_CAPABILITY_PCIEXP, SlotCapability);\r
381 Status = PciIoDevice->PciIo.Pci.Read (\r
382 &PciIoDevice->PciIo,\r
383 EfiPciIoWidthUint32,\r
384 Offset,\r
385 1,\r
386 &SlotCapability\r
387 );\r
388 if (EFI_ERROR (Status)) {\r
389 return FALSE;\r
390 }\r
391\r
392 //\r
393 // Check the contents of the register\r
394 //\r
395 if (SlotCapability.Bits.HotPlugCapable) {\r
396 return TRUE;\r
397 }\r
398 return FALSE;\r
399}\r
400\r
9060e3ec 401/**\r
402 Get resource padding if the specified PCI bridge is a hot plug bus.\r
403\r
404 @param PciIoDevice PCI bridge instance.\r
405\r
406**/\r
407VOID\r
408GetResourcePaddingForHpb (\r
409 IN PCI_IO_DEVICE *PciIoDevice\r
410 )\r
411{\r
412 EFI_STATUS Status;\r
413 EFI_HPC_STATE State;\r
414 UINT64 PciAddress;\r
415 EFI_HPC_PADDING_ATTRIBUTES Attributes;\r
416 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptors;\r
417\r
418 if (IsPciHotPlugBus (PciIoDevice)) {\r
419 //\r
420 // If PCI-PCI bridge device is PCI Hot Plug bus.\r
421 //\r
422 PciAddress = EFI_PCI_ADDRESS (PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber, 0);\r
423 Status = gPciHotPlugInit->GetResourcePadding (\r
424 gPciHotPlugInit,\r
425 PciIoDevice->DevicePath,\r
426 PciAddress,\r
427 &State,\r
428 (VOID **) &Descriptors,\r
429 &Attributes\r
430 );\r
431\r
432 if (EFI_ERROR (Status)) {\r
433 return;\r
434 }\r
435\r
436 if ((State & EFI_HPC_STATE_ENABLED) != 0 && (State & EFI_HPC_STATE_INITIALIZED) != 0) {\r
437 PciIoDevice->ResourcePaddingDescriptors = Descriptors;\r
438 PciIoDevice->PaddingAttributes = Attributes;\r
439 }\r
440\r
441 return;\r
442 }\r
443}\r
444\r
445/**\r
446 Test whether PCI device is hot plug bus.\r
447\r
448 @param PciIoDevice PCI device instance.\r
449\r
450 @retval TRUE PCI device is a hot plug bus.\r
451 @retval FALSE PCI device is not a hot plug bus.\r
452\r
453**/\r
454BOOLEAN\r
455IsPciHotPlugBus (\r
456 PCI_IO_DEVICE *PciIoDevice\r
457 )\r
458{\r
459 if (IsSHPC (PciIoDevice)) {\r
460 //\r
461 // If the PPB has the hot plug controller build-in,\r
462 // then return TRUE;\r
463 //\r
464 return TRUE;\r
465 }\r
466\r
ffdd3376
LE
467 if (SupportsPcieHotplug (PciIoDevice)) {\r
468 //\r
469 // If the PPB is a PCIe root complex port or a switch downstream port, and\r
470 // implements a hot-plug capable slot, then also return TRUE.\r
471 //\r
472 return TRUE;\r
473 }\r
474\r
9060e3ec 475 //\r
476 // Otherwise, see if it is a Root HPC\r
477 //\r
478 if(IsRootPciHotPlugBus (PciIoDevice->DevicePath, NULL)) {\r
479 return TRUE;\r
480 }\r
481\r
482 return FALSE;\r
483}\r
484\r