MdeModulePkg/PciBusDxe: catch unimplemented extended config space reads
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciBusDxe / PciCommand.c
1 /** @file\r
2   PCI command register operations supporting functions implementation for PCI Bus module.\r
3 \r
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6 \r
7 **/\r
8 \r
9 #include "PciBus.h"\r
10 \r
11 /**\r
12   Operate the PCI register via PciIo function interface.\r
13 \r
14   @param PciIoDevice    Pointer to instance of PCI_IO_DEVICE.\r
15   @param Command        Operator command.\r
16   @param Offset         The address within the PCI configuration space for the PCI controller.\r
17   @param Operation      Type of Operation.\r
18   @param PtrCommand     Return buffer holding old PCI command, if operation is not EFI_SET_REGISTER.\r
19 \r
20   @return Status of PciIo operation.\r
21 \r
22 **/\r
23 EFI_STATUS\r
24 PciOperateRegister (\r
25   IN  PCI_IO_DEVICE *PciIoDevice,\r
26   IN  UINT16        Command,\r
27   IN  UINT8         Offset,\r
28   IN  UINT8         Operation,\r
29   OUT UINT16        *PtrCommand\r
30   )\r
31 {\r
32   UINT16              OldCommand;\r
33   EFI_STATUS          Status;\r
34   EFI_PCI_IO_PROTOCOL *PciIo;\r
35 \r
36   OldCommand  = 0;\r
37   PciIo       = &PciIoDevice->PciIo;\r
38 \r
39   if (Operation != EFI_SET_REGISTER) {\r
40     Status = PciIo->Pci.Read (\r
41                           PciIo,\r
42                           EfiPciIoWidthUint16,\r
43                           Offset,\r
44                           1,\r
45                           &OldCommand\r
46                           );\r
47 \r
48     if (Operation == EFI_GET_REGISTER) {\r
49       *PtrCommand = OldCommand;\r
50       return Status;\r
51     }\r
52   }\r
53 \r
54   if (Operation == EFI_ENABLE_REGISTER) {\r
55     OldCommand = (UINT16) (OldCommand | Command);\r
56   } else if (Operation == EFI_DISABLE_REGISTER) {\r
57     OldCommand = (UINT16) (OldCommand & ~(Command));\r
58   } else {\r
59     OldCommand = Command;\r
60   }\r
61 \r
62   return PciIo->Pci.Write (\r
63                       PciIo,\r
64                       EfiPciIoWidthUint16,\r
65                       Offset,\r
66                       1,\r
67                       &OldCommand\r
68                       );\r
69 }\r
70 \r
71 /**\r
72   Check the capability supporting by given device.\r
73 \r
74   @param PciIoDevice   Pointer to instance of PCI_IO_DEVICE.\r
75 \r
76   @retval TRUE         Capability supported.\r
77   @retval FALSE        Capability not supported.\r
78 \r
79 **/\r
80 BOOLEAN\r
81 PciCapabilitySupport (\r
82   IN PCI_IO_DEVICE  *PciIoDevice\r
83   )\r
84 {\r
85   if ((PciIoDevice->Pci.Hdr.Status & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
86     return TRUE;\r
87   }\r
88 \r
89   return FALSE;\r
90 }\r
91 \r
92 /**\r
93   Locate capability register block per capability ID.\r
94 \r
95   @param PciIoDevice       A pointer to the PCI_IO_DEVICE.\r
96   @param CapId             The capability ID.\r
97   @param Offset            A pointer to the offset returned.\r
98   @param NextRegBlock      A pointer to the next block returned.\r
99 \r
100   @retval EFI_SUCCESS      Successfully located capability register block.\r
101   @retval EFI_UNSUPPORTED  Pci device does not support capability.\r
102   @retval EFI_NOT_FOUND    Pci device support but can not find register block.\r
103 \r
104 **/\r
105 EFI_STATUS\r
106 LocateCapabilityRegBlock (\r
107   IN PCI_IO_DEVICE  *PciIoDevice,\r
108   IN UINT8          CapId,\r
109   IN OUT UINT8      *Offset,\r
110   OUT UINT8         *NextRegBlock OPTIONAL\r
111   )\r
112 {\r
113   UINT8   CapabilityPtr;\r
114   UINT16  CapabilityEntry;\r
115   UINT8   CapabilityID;\r
116 \r
117   //\r
118   // To check the capability of this device supports\r
119   //\r
120   if (!PciCapabilitySupport (PciIoDevice)) {\r
121     return EFI_UNSUPPORTED;\r
122   }\r
123 \r
124   if (*Offset != 0) {\r
125     CapabilityPtr = *Offset;\r
126   } else {\r
127 \r
128     CapabilityPtr = 0;\r
129     if (IS_CARDBUS_BRIDGE (&PciIoDevice->Pci)) {\r
130 \r
131       PciIoDevice->PciIo.Pci.Read (\r
132                                &PciIoDevice->PciIo,\r
133                                EfiPciIoWidthUint8,\r
134                                EFI_PCI_CARDBUS_BRIDGE_CAPABILITY_PTR,\r
135                                1,\r
136                                &CapabilityPtr\r
137                                );\r
138     } else {\r
139 \r
140       PciIoDevice->PciIo.Pci.Read (\r
141                                &PciIoDevice->PciIo,\r
142                                EfiPciIoWidthUint8,\r
143                                PCI_CAPBILITY_POINTER_OFFSET,\r
144                                1,\r
145                                &CapabilityPtr\r
146                                );\r
147     }\r
148   }\r
149 \r
150   while ((CapabilityPtr >= 0x40) && ((CapabilityPtr & 0x03) == 0x00)) {\r
151     PciIoDevice->PciIo.Pci.Read (\r
152                              &PciIoDevice->PciIo,\r
153                              EfiPciIoWidthUint16,\r
154                              CapabilityPtr,\r
155                              1,\r
156                              &CapabilityEntry\r
157                              );\r
158 \r
159     CapabilityID = (UINT8) CapabilityEntry;\r
160 \r
161     if (CapabilityID == CapId) {\r
162       *Offset = CapabilityPtr;\r
163       if (NextRegBlock != NULL) {\r
164         *NextRegBlock = (UINT8) (CapabilityEntry >> 8);\r
165       }\r
166 \r
167       return EFI_SUCCESS;\r
168     }\r
169 \r
170     //\r
171     // Certain PCI device may incorrectly have capability pointing to itself,\r
172     // break to avoid dead loop.\r
173     //\r
174     if (CapabilityPtr == (UINT8) (CapabilityEntry >> 8)) {\r
175       break;\r
176     }\r
177 \r
178     CapabilityPtr = (UINT8) (CapabilityEntry >> 8);\r
179   }\r
180 \r
181   return EFI_NOT_FOUND;\r
182 }\r
183 \r
184 /**\r
185   Locate PciExpress capability register block per capability ID.\r
186 \r
187   @param PciIoDevice       A pointer to the PCI_IO_DEVICE.\r
188   @param CapId             The capability ID.\r
189   @param Offset            A pointer to the offset returned.\r
190   @param NextRegBlock      A pointer to the next block returned.\r
191 \r
192   @retval EFI_SUCCESS      Successfully located capability register block.\r
193   @retval EFI_UNSUPPORTED  Pci device does not support capability.\r
194   @retval EFI_NOT_FOUND    Pci device support but can not find register block.\r
195 \r
196 **/\r
197 EFI_STATUS\r
198 LocatePciExpressCapabilityRegBlock (\r
199   IN     PCI_IO_DEVICE *PciIoDevice,\r
200   IN     UINT16        CapId,\r
201   IN OUT UINT32        *Offset,\r
202      OUT UINT32        *NextRegBlock OPTIONAL\r
203   )\r
204 {\r
205   EFI_STATUS           Status;\r
206   UINT32               CapabilityPtr;\r
207   UINT32               CapabilityEntry;\r
208   UINT16               CapabilityID;\r
209 \r
210   //\r
211   // To check the capability of this device supports\r
212   //\r
213   if (!PciIoDevice->IsPciExp) {\r
214     return EFI_UNSUPPORTED;\r
215   }\r
216 \r
217   if (*Offset != 0) {\r
218     CapabilityPtr = *Offset;\r
219   } else {\r
220     CapabilityPtr = EFI_PCIE_CAPABILITY_BASE_OFFSET;\r
221   }\r
222 \r
223   while (CapabilityPtr != 0) {\r
224     //\r
225     // Mask it to DWORD alignment per PCI spec\r
226     //\r
227     CapabilityPtr &= 0xFFC;\r
228     Status = PciIoDevice->PciIo.Pci.Read (\r
229                                       &PciIoDevice->PciIo,\r
230                                       EfiPciIoWidthUint32,\r
231                                       CapabilityPtr,\r
232                                       1,\r
233                                       &CapabilityEntry\r
234                                       );\r
235     if (EFI_ERROR (Status)) {\r
236       break;\r
237     }\r
238 \r
239     if (CapabilityEntry == MAX_UINT32) {\r
240       DEBUG ((\r
241         DEBUG_WARN,\r
242         "%a: [%02x|%02x|%02x] failed to access config space at offset 0x%x\n",\r
243         __FUNCTION__,\r
244         PciIoDevice->BusNumber,\r
245         PciIoDevice->DeviceNumber,\r
246         PciIoDevice->FunctionNumber,\r
247         CapabilityPtr\r
248         ));\r
249       break;\r
250     }\r
251 \r
252     CapabilityID = (UINT16) CapabilityEntry;\r
253 \r
254     if (CapabilityID == CapId) {\r
255       *Offset = CapabilityPtr;\r
256       if (NextRegBlock != NULL) {\r
257         *NextRegBlock = (CapabilityEntry >> 20) & 0xFFF;\r
258       }\r
259 \r
260       return EFI_SUCCESS;\r
261     }\r
262 \r
263     CapabilityPtr = (CapabilityEntry >> 20) & 0xFFF;\r
264   }\r
265 \r
266   return EFI_NOT_FOUND;\r
267 }\r