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