1 /*-
2 * Copyright (c) 2009-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include "efx.h"
35 #include "efx_impl.h"
36
37
38 #if EFSYS_OPT_VPD
39
40 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
41
42 #include "ef10_tlv_layout.h"
43
44 __checkReturn efx_rc_t
ef10_vpd_init(__in efx_nic_t * enp)45 ef10_vpd_init(
46 __in efx_nic_t *enp)
47 {
48 caddr_t svpd;
49 size_t svpd_size;
50 uint32_t pci_pf;
51 uint32_t tag;
52 efx_rc_t rc;
53
54 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
55 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
56 enp->en_family == EFX_FAMILY_MEDFORD);
57
58 if (enp->en_nic_cfg.enc_vpd_is_global) {
59 tag = TLV_TAG_GLOBAL_STATIC_VPD;
60 } else {
61 pci_pf = enp->en_nic_cfg.enc_pf;
62 tag = TLV_TAG_PF_STATIC_VPD(pci_pf);
63 }
64
65 /*
66 * The VPD interface exposes VPD resources from the combined static and
67 * dynamic VPD storage. As the static VPD configuration should *never*
68 * change, we can cache it.
69 */
70 svpd = NULL;
71 svpd_size = 0;
72 rc = ef10_nvram_partn_read_tlv(enp,
73 NVRAM_PARTITION_TYPE_STATIC_CONFIG,
74 tag, &svpd, &svpd_size);
75 if (rc != 0) {
76 if (rc == EACCES) {
77 /* Unprivileged functions cannot access VPD */
78 goto out;
79 }
80 goto fail1;
81 }
82
83 if (svpd != NULL && svpd_size > 0) {
84 if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0)
85 goto fail2;
86 }
87
88 enp->en_arch.ef10.ena_svpd = svpd;
89 enp->en_arch.ef10.ena_svpd_length = svpd_size;
90
91 out:
92 return (0);
93
94 fail2:
95 EFSYS_PROBE(fail2);
96
97 EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
98 fail1:
99 EFSYS_PROBE1(fail1, efx_rc_t, rc);
100
101 return (rc);
102 }
103
104 __checkReturn efx_rc_t
ef10_vpd_size(__in efx_nic_t * enp,__out size_t * sizep)105 ef10_vpd_size(
106 __in efx_nic_t *enp,
107 __out size_t *sizep)
108 {
109 efx_rc_t rc;
110
111 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
112 enp->en_family == EFX_FAMILY_MEDFORD);
113
114 /*
115 * This function returns the total size the user should allocate
116 * for all VPD operations. We've already cached the static vpd,
117 * so we just need to return an upper bound on the dynamic vpd,
118 * which is the size of the DYNAMIC_CONFIG partition.
119 */
120 if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
121 sizep, NULL, NULL, NULL)) != 0)
122 goto fail1;
123
124 return (0);
125
126 fail1:
127 EFSYS_PROBE1(fail1, efx_rc_t, rc);
128
129 return (rc);
130 }
131
132 __checkReturn efx_rc_t
ef10_vpd_read(__in efx_nic_t * enp,__out_bcount (size)caddr_t data,__in size_t size)133 ef10_vpd_read(
134 __in efx_nic_t *enp,
135 __out_bcount(size) caddr_t data,
136 __in size_t size)
137 {
138 caddr_t dvpd;
139 size_t dvpd_size;
140 uint32_t pci_pf;
141 uint32_t tag;
142 efx_rc_t rc;
143
144 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
145 enp->en_family == EFX_FAMILY_MEDFORD);
146
147 if (enp->en_nic_cfg.enc_vpd_is_global) {
148 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
149 } else {
150 pci_pf = enp->en_nic_cfg.enc_pf;
151 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
152 }
153
154 if ((rc = ef10_nvram_partn_read_tlv(enp,
155 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
156 tag, &dvpd, &dvpd_size)) != 0)
157 goto fail1;
158
159 if (dvpd_size > size) {
160 rc = ENOSPC;
161 goto fail2;
162 }
163 memcpy(data, dvpd, dvpd_size);
164
165 /* Pad data with all-1s, consistent with update operations */
166 memset(data + dvpd_size, 0xff, size - dvpd_size);
167
168 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
169
170 return (0);
171
172 fail2:
173 EFSYS_PROBE(fail2);
174
175 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
176 fail1:
177 EFSYS_PROBE1(fail1, efx_rc_t, rc);
178
179 return (rc);
180 }
181
182 __checkReturn efx_rc_t
ef10_vpd_verify(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)183 ef10_vpd_verify(
184 __in efx_nic_t *enp,
185 __in_bcount(size) caddr_t data,
186 __in size_t size)
187 {
188 efx_vpd_tag_t stag;
189 efx_vpd_tag_t dtag;
190 efx_vpd_keyword_t skey;
191 efx_vpd_keyword_t dkey;
192 unsigned int scont;
193 unsigned int dcont;
194 efx_rc_t rc;
195
196 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
197 enp->en_family == EFX_FAMILY_MEDFORD);
198
199 /*
200 * Strictly you could take the view that dynamic vpd is optional.
201 * Instead, to conform more closely to the read/verify/reinit()
202 * paradigm, we require dynamic vpd. ef10_vpd_reinit() will
203 * reinitialize it as required.
204 */
205 if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
206 goto fail1;
207
208 /*
209 * Verify that there is no duplication between the static and
210 * dynamic cfg sectors.
211 */
212 if (enp->en_arch.ef10.ena_svpd_length == 0)
213 goto done;
214
215 dcont = 0;
216 _NOTE(CONSTANTCONDITION)
217 while (1) {
218 if ((rc = efx_vpd_hunk_next(data, size, &dtag,
219 &dkey, NULL, NULL, &dcont)) != 0)
220 goto fail2;
221 if (dcont == 0)
222 break;
223
224 /*
225 * Skip the RV keyword. It should be present in both the static
226 * and dynamic cfg sectors.
227 */
228 if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
229 continue;
230
231 scont = 0;
232 _NOTE(CONSTANTCONDITION)
233 while (1) {
234 if ((rc = efx_vpd_hunk_next(
235 enp->en_arch.ef10.ena_svpd,
236 enp->en_arch.ef10.ena_svpd_length, &stag, &skey,
237 NULL, NULL, &scont)) != 0)
238 goto fail3;
239 if (scont == 0)
240 break;
241
242 if (stag == dtag && skey == dkey) {
243 rc = EEXIST;
244 goto fail4;
245 }
246 }
247 }
248
249 done:
250 return (0);
251
252 fail4:
253 EFSYS_PROBE(fail4);
254 fail3:
255 EFSYS_PROBE(fail3);
256 fail2:
257 EFSYS_PROBE(fail2);
258 fail1:
259 EFSYS_PROBE1(fail1, efx_rc_t, rc);
260
261 return (rc);
262 }
263
264 __checkReturn efx_rc_t
ef10_vpd_reinit(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)265 ef10_vpd_reinit(
266 __in efx_nic_t *enp,
267 __in_bcount(size) caddr_t data,
268 __in size_t size)
269 {
270 boolean_t wantpid;
271 efx_rc_t rc;
272
273 /*
274 * Only create an ID string if the dynamic cfg doesn't have one
275 */
276 if (enp->en_arch.ef10.ena_svpd_length == 0)
277 wantpid = B_TRUE;
278 else {
279 unsigned int offset;
280 uint8_t length;
281
282 rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
283 enp->en_arch.ef10.ena_svpd_length,
284 EFX_VPD_ID, 0, &offset, &length);
285 if (rc == 0)
286 wantpid = B_FALSE;
287 else if (rc == ENOENT)
288 wantpid = B_TRUE;
289 else
290 goto fail1;
291 }
292
293 if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
294 goto fail2;
295
296 return (0);
297
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
ef10_vpd_get(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size,__inout efx_vpd_value_t * evvp)307 ef10_vpd_get(
308 __in efx_nic_t *enp,
309 __in_bcount(size) caddr_t data,
310 __in size_t size,
311 __inout efx_vpd_value_t *evvp)
312 {
313 unsigned int offset;
314 uint8_t length;
315 efx_rc_t rc;
316
317 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
318 enp->en_family == EFX_FAMILY_MEDFORD);
319
320 /* Attempt to satisfy the request from svpd first */
321 if (enp->en_arch.ef10.ena_svpd_length > 0) {
322 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
323 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
324 evvp->evv_keyword, &offset, &length)) == 0) {
325 evvp->evv_length = length;
326 memcpy(evvp->evv_value,
327 enp->en_arch.ef10.ena_svpd + offset, length);
328 return (0);
329 } else if (rc != ENOENT)
330 goto fail1;
331 }
332
333 /* And then from the provided data buffer */
334 if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
335 evvp->evv_keyword, &offset, &length)) != 0) {
336 if (rc == ENOENT)
337 return (rc);
338 goto fail2;
339 }
340
341 evvp->evv_length = length;
342 memcpy(evvp->evv_value, data + offset, length);
343
344 return (0);
345
346 fail2:
347 EFSYS_PROBE(fail2);
348 fail1:
349 EFSYS_PROBE1(fail1, efx_rc_t, rc);
350
351 return (rc);
352 }
353
354 __checkReturn efx_rc_t
ef10_vpd_set(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size,__in efx_vpd_value_t * evvp)355 ef10_vpd_set(
356 __in efx_nic_t *enp,
357 __in_bcount(size) caddr_t data,
358 __in size_t size,
359 __in efx_vpd_value_t *evvp)
360 {
361 efx_rc_t rc;
362
363 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
364 enp->en_family == EFX_FAMILY_MEDFORD);
365
366 /* If the provided (tag,keyword) exists in svpd, then it is readonly */
367 if (enp->en_arch.ef10.ena_svpd_length > 0) {
368 unsigned int offset;
369 uint8_t length;
370
371 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
372 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
373 evvp->evv_keyword, &offset, &length)) == 0) {
374 rc = EACCES;
375 goto fail1;
376 }
377 }
378
379 if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
380 goto fail2;
381
382 return (0);
383
384 fail2:
385 EFSYS_PROBE(fail2);
386 fail1:
387 EFSYS_PROBE1(fail1, efx_rc_t, rc);
388
389 return (rc);
390 }
391
392 __checkReturn efx_rc_t
ef10_vpd_next(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size,__out efx_vpd_value_t * evvp,__inout unsigned int * contp)393 ef10_vpd_next(
394 __in efx_nic_t *enp,
395 __in_bcount(size) caddr_t data,
396 __in size_t size,
397 __out efx_vpd_value_t *evvp,
398 __inout unsigned int *contp)
399 {
400 _NOTE(ARGUNUSED(enp, data, size, evvp, contp))
401
402 return (ENOTSUP);
403 }
404
405 __checkReturn efx_rc_t
ef10_vpd_write(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)406 ef10_vpd_write(
407 __in efx_nic_t *enp,
408 __in_bcount(size) caddr_t data,
409 __in size_t size)
410 {
411 size_t vpd_length;
412 uint32_t pci_pf;
413 uint32_t tag;
414 efx_rc_t rc;
415
416 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
417 enp->en_family == EFX_FAMILY_MEDFORD);
418
419 if (enp->en_nic_cfg.enc_vpd_is_global) {
420 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
421 } else {
422 pci_pf = enp->en_nic_cfg.enc_pf;
423 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
424 }
425
426 /* Determine total length of new dynamic VPD */
427 if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
428 goto fail1;
429
430 /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
431 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
432 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
433 tag, data, vpd_length, B_TRUE)) != 0) {
434 goto fail2;
435 }
436
437 return (0);
438
439 fail2:
440 EFSYS_PROBE(fail2);
441
442 fail1:
443 EFSYS_PROBE1(fail1, efx_rc_t, rc);
444
445 return (rc);
446 }
447
448 void
ef10_vpd_fini(__in efx_nic_t * enp)449 ef10_vpd_fini(
450 __in efx_nic_t *enp)
451 {
452 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
453 enp->en_family == EFX_FAMILY_MEDFORD);
454
455 if (enp->en_arch.ef10.ena_svpd_length > 0) {
456 EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length,
457 enp->en_arch.ef10.ena_svpd);
458
459 enp->en_arch.ef10.ena_svpd = NULL;
460 enp->en_arch.ef10.ena_svpd_length = 0;
461 }
462 }
463
464 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
465
466 #endif /* EFSYS_OPT_VPD */
467