common/sfc_efx/base: fix dereferencing null pointer
[dpdk.git] / drivers / common / sfc_efx / base / efx_pci.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  *
3  * Copyright(c) 2019-2021 Xilinx, Inc.
4  * Copyright(c) 2019 Solarflare Communications Inc.
5  */
6
7 #include "efx.h"
8 #include "efx_impl.h"
9
10 #if EFSYS_OPT_PCI
11
12         __checkReturn                   efx_rc_t
13 efx_pci_config_next_ext_cap(
14         __in                            efsys_pci_config_t *espcp,
15         __in                            const efx_pci_ops_t *epop,
16         __inout                         size_t *offsetp)
17 {
18         efx_dword_t hdr;
19         efx_rc_t rc = 0;
20         size_t next;
21
22         if (offsetp == NULL) {
23                 rc = EINVAL;
24                 goto fail1;
25         }
26
27         if (*offsetp == 0) {
28                 *offsetp = ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE;
29         } else {
30                 rc = epop->epo_config_readd(espcp, *offsetp +
31                                 (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
32                                 &hdr);
33                 if (rc != 0) {
34                         rc = EIO;
35                         goto fail2;
36                 }
37
38                 next = EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_NEXT);
39                 if (next < ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE)
40                         rc = ENOENT;
41                 else
42                         *offsetp = next;
43         }
44
45         /*
46          * Returns 0 if the next capability is present otherwise ENOENT
47          * indicating that the function finished correctly.
48          */
49         return (rc);
50
51 fail2:
52         EFSYS_PROBE(fail2);
53 fail1:
54         EFSYS_PROBE1(fail1, efx_rc_t, rc);
55
56         return (rc);
57 }
58
59         __checkReturn                   efx_rc_t
60 efx_pci_config_find_next_ext_cap(
61         __in                            efsys_pci_config_t *espcp,
62         __in                            const efx_pci_ops_t *epop,
63         __in                            uint16_t cap_id,
64         __inout                         size_t *offsetp)
65 {
66         efx_dword_t hdr;
67         size_t position;
68         efx_rc_t rc;
69
70         if (offsetp == NULL) {
71                 rc = EINVAL;
72                 goto fail1;
73         }
74
75         position = *offsetp;
76
77         while (1) {
78                 rc = efx_pci_config_next_ext_cap(espcp, epop, &position);
79                 if (rc != 0) {
80                         if (rc == ENOENT)
81                                 break;
82                         else
83                                 goto fail2;
84                 }
85
86                 rc = epop->epo_config_readd(espcp, position +
87                                 (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
88                                 &hdr);
89                 if (rc != 0) {
90                         rc = EIO;
91                         goto fail3;
92                 }
93
94                 if (EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_ID) ==
95                     cap_id) {
96                         *offsetp = position;
97                         rc = 0;
98                         break;
99                 }
100         }
101
102         /*
103          * Returns 0 if found otherwise ENOENT indicating that search finished
104          * correctly.
105          */
106         return (rc);
107
108 fail3:
109         EFSYS_PROBE(fail3);
110 fail2:
111         EFSYS_PROBE(fail2);
112 fail1:
113         EFSYS_PROBE1(fail1, efx_rc_t, rc);
114
115         return (rc);
116 }
117
118         __checkReturn                   efx_rc_t
119 efx_pci_find_next_xilinx_cap_table(
120         __in                            efsys_pci_config_t *espcp,
121         __in                            const efx_pci_ops_t *epop,
122         __inout                         size_t *pci_cap_offsetp,
123         __out                           unsigned int *xilinx_tbl_barp,
124         __out                           efsys_dma_addr_t *xilinx_tbl_offsetp)
125 {
126         size_t cap_offset;
127         efx_rc_t rc;
128
129         if (pci_cap_offsetp == NULL) {
130                 rc = EINVAL;
131                 goto fail1;
132         }
133
134         cap_offset = *pci_cap_offsetp;
135
136         while (1) {
137                 unsigned int tbl_bar;
138                 efsys_dma_addr_t tbl_offset;
139
140                 rc = efx_pci_config_find_next_ext_cap(espcp, epop,
141                                 ESE_GZ_PCI_EXPRESS_XCAP_ID_VNDR, &cap_offset);
142                 if (rc != 0) {
143                         if (rc == ENOENT)
144                                 break;
145                         else
146                                 goto fail2;
147                 }
148
149                 /*
150                  * The found extended PCI capability is a vendor-specific
151                  * capability, but not necessarily a Xilinx capabilities table
152                  * locator. Try to read it and skip it if the capability is
153                  * not the locator.
154                  */
155                 rc = efx_pci_read_ext_cap_xilinx_table(espcp, epop, cap_offset,
156                                                        &tbl_bar, &tbl_offset);
157                 if (rc == 0) {
158                         *xilinx_tbl_barp = tbl_bar;
159                         *xilinx_tbl_offsetp = tbl_offset;
160                         *pci_cap_offsetp = cap_offset;
161                         break;
162                 } else {
163                         if (rc == ENOENT)
164                                 continue;
165                         else
166                                 goto fail3;
167                 }
168         }
169
170         /*
171          * Returns 0 if found otherwise ENOENT indicating that search finished
172          * correctly.
173          */
174         return (rc);
175
176 fail3:
177         EFSYS_PROBE(fail3);
178 fail2:
179         EFSYS_PROBE(fail2);
180 fail1:
181         EFSYS_PROBE1(fail1, efx_rc_t, rc);
182
183         return (rc);
184 }
185
186         __checkReturn                   efx_rc_t
187 efx_pci_read_ext_cap_xilinx_table(
188         __in                            efsys_pci_config_t *espcp,
189         __in                            const efx_pci_ops_t *epop,
190         __in                            size_t cap_offset,
191         __out                           unsigned int *barp,
192         __out                           efsys_dma_addr_t *offsetp)
193 {
194         size_t vsec_offset = cap_offset + ESE_GZ_PCI_EXPRESS_XCAP_HDR_SIZE;
195         efx_dword_t cap_hdr;
196         efx_oword_t vsec;
197         uint32_t vsec_len;
198         uint32_t vsec_id;
199         uint32_t vsec_rev;
200         uint32_t offset_low;
201         uint32_t offset_high = 0;
202         unsigned int bar;
203         efsys_dma_addr_t offset;
204         efx_rc_t rc;
205
206         rc = epop->epo_config_readd(espcp, cap_offset +
207                                (EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
208                                &cap_hdr);
209         if (rc != 0) {
210                 rc = EIO;
211                 goto fail1;
212         }
213
214         if (EFX_DWORD_FIELD(cap_hdr, ESF_GZ_PCI_EXPRESS_XCAP_VER) !=
215             ESE_GZ_PCI_EXPRESS_XCAP_VER_VSEC) {
216                 rc = EINVAL;
217                 goto fail2;
218         }
219
220         rc = epop->epo_config_readd(espcp, vsec_offset +
221                                (EFX_LOW_BIT(ESF_GZ_VSEC_ID) / 8),
222                                &vsec.eo_dword[0]);
223         if (rc != 0) {
224                 rc = EIO;
225                 goto fail3;
226         }
227
228         vsec_len = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_LEN);
229         vsec_id = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_ID);
230         vsec_rev = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_VER);
231
232         /*
233          * Condition of the vendor-specific extended PCI capability not being
234          * a Xilinx capabilities table locator.
235          */
236         if (vsec_id != ESE_GZ_XILINX_VSEC_ID) {
237                 rc = ENOENT;
238                 goto fail4;
239         }
240
241         if (vsec_rev != ESE_GZ_VSEC_VER_XIL_CFGBAR ||
242             vsec_len < ESE_GZ_VSEC_LEN_MIN) {
243                 rc = EINVAL;
244                 goto fail5;
245         }
246
247         rc = epop->epo_config_readd(espcp, vsec_offset +
248                                (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_BAR) / 8),
249                                &vsec.eo_dword[1]);
250         if (rc != 0) {
251                 rc = EIO;
252                 goto fail6;
253         }
254
255         bar = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_BAR);
256         offset_low = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_LO);
257
258         if (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT) {
259                 rc = epop->epo_config_readd(espcp, vsec_offset +
260                                 (EFX_LOW_BIT(ESF_GZ_VSEC_TBL_OFF_HI) / 8),
261                                 &vsec.eo_dword[2]);
262                 if (rc != 0) {
263                         rc = EIO;
264                         goto fail7;
265                 }
266
267                 offset_high = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_HI);
268         }
269
270         /* High bits of low offset are discarded by the shift */
271         offset = offset_low << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT;
272
273         /*
274          * Avoid the 'left shift count >= width of type' warning on systems
275          * without uint64_t support.
276          */
277 #if EFSYS_HAS_UINT64
278         offset |= (uint64_t)offset_high << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT;
279 #else
280         _NOTE(ARGUNUSED(offset_high))
281 #endif
282
283         *offsetp = offset;
284         *barp = bar;
285
286         return (0);
287
288 fail7:
289         EFSYS_PROBE(fail7);
290 fail6:
291         EFSYS_PROBE(fail6);
292 fail5:
293         EFSYS_PROBE(fail5);
294 fail4:
295         EFSYS_PROBE(fail4);
296 fail3:
297         EFSYS_PROBE(fail3);
298 fail2:
299         EFSYS_PROBE(fail2);
300 fail1:
301         EFSYS_PROBE1(fail1, efx_rc_t, rc);
302
303         return (rc);
304 }
305
306         __checkReturn                   efx_rc_t
307 efx_pci_xilinx_cap_tbl_find(
308         __in                            efsys_bar_t *esbp,
309         __in                            uint32_t format_id,
310         __in                            boolean_t skip_first,
311         __inout                         efsys_dma_addr_t *entry_offsetp)
312 {
313         efsys_dma_addr_t offset;
314         boolean_t skip = skip_first;
315         efx_qword_t header;
316         uint32_t format;
317         uint32_t last;
318         efx_rc_t rc;
319
320         if (entry_offsetp == NULL) {
321                 rc = EINVAL;
322                 goto fail1;
323         }
324
325         offset = *entry_offsetp;
326         rc = ENOENT;
327         /*
328          * SF-119689-TC Riverhead Host Interface section 4.2.2.
329          * describes the following discovery steps.
330          */
331         do {
332                 /*
333                  * Xilinx Capabilities Table requires 32bit aligned reads.
334                  * See SF-119689-TC section 4.2.2 "Discovery Steps".
335                  */
336                 EFSYS_BAR_READD(esbp, offset +
337                                 (EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_FORMAT) / 8),
338                                 &header.eq_dword[0], B_FALSE);
339                 EFSYS_BAR_READD(esbp, offset +
340                                 (EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_SIZE) / 8),
341                                 &header.eq_dword[1], B_FALSE);
342
343                 format = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_FORMAT);
344                 last = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_LAST);
345
346                 if (skip == B_FALSE && format == format_id) {
347                         *entry_offsetp = offset;
348                         rc = 0;
349                         break;
350                 }
351
352                 offset += EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_SIZE);
353                 skip = B_FALSE;
354         } while (last == B_FALSE);
355
356         /*
357          * Returns 0 if found otherwise ENOENT indicating that
358          * search finished correctly.
359          */
360         return (rc);
361
362 fail1:
363         EFSYS_PROBE1(fail1, efx_rc_t, rc);
364
365         return (rc);
366 }
367
368 #endif /* EFSYS_OPT_PCI */