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 #include "efx.h"
33 #include "efx_impl.h"
34
35 #if EFSYS_OPT_VPD
36
37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
38
39 #include "ef10_tlv_layout.h"
40
41 __checkReturn efx_rc_t
ef10_vpd_init(__in efx_nic_t * enp)42 ef10_vpd_init(
43 __in efx_nic_t *enp)
44 {
45 caddr_t svpd;
46 size_t svpd_size;
47 uint32_t pci_pf;
48 uint32_t tag;
49 efx_rc_t rc;
50
51 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE);
52 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
53 enp->en_family == EFX_FAMILY_MEDFORD ||
54 enp->en_family == EFX_FAMILY_MEDFORD2);
55
56 if (enp->en_nic_cfg.enc_vpd_is_global) {
57 tag = TLV_TAG_GLOBAL_STATIC_VPD;
58 } else {
59 pci_pf = enp->en_nic_cfg.enc_pf;
60 tag = TLV_TAG_PF_STATIC_VPD(pci_pf);
61 }
62
63 /*
64 * The VPD interface exposes VPD resources from the combined static and
65 * dynamic VPD storage. As the static VPD configuration should *never*
66 * change, we can cache it.
67 */
68 svpd = NULL;
69 svpd_size = 0;
70 rc = ef10_nvram_partn_read_tlv(enp,
71 NVRAM_PARTITION_TYPE_STATIC_CONFIG,
72 tag, &svpd, &svpd_size);
73 if (rc != 0) {
74 if (rc == EACCES) {
75 /* Unprivileged functions cannot access VPD */
76 goto out;
77 }
78 goto fail1;
79 }
80
81 if (svpd != NULL && svpd_size > 0) {
82 if ((rc = efx_vpd_hunk_verify(svpd, svpd_size, NULL)) != 0)
83 goto fail2;
84 }
85
86 enp->en_arch.ef10.ena_svpd = svpd;
87 enp->en_arch.ef10.ena_svpd_length = svpd_size;
88
89 out:
90 return (0);
91
92 fail2:
93 EFSYS_PROBE(fail2);
94
95 EFSYS_KMEM_FREE(enp->en_esip, svpd_size, svpd);
96 fail1:
97 EFSYS_PROBE1(fail1, efx_rc_t, rc);
98
99 return (rc);
100 }
101
102 __checkReturn efx_rc_t
ef10_vpd_size(__in efx_nic_t * enp,__out size_t * sizep)103 ef10_vpd_size(
104 __in efx_nic_t *enp,
105 __out size_t *sizep)
106 {
107 efx_rc_t rc;
108
109 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
110 enp->en_family == EFX_FAMILY_MEDFORD ||
111 enp->en_family == EFX_FAMILY_MEDFORD2);
112
113 /*
114 * This function returns the total size the user should allocate
115 * for all VPD operations. We've already cached the static vpd,
116 * so we just need to return an upper bound on the dynamic vpd,
117 * which is the size of the DYNAMIC_CONFIG partition.
118 */
119 if ((rc = efx_mcdi_nvram_info(enp, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
120 sizep, NULL, NULL, NULL)) != 0)
121 goto fail1;
122
123 return (0);
124
125 fail1:
126 EFSYS_PROBE1(fail1, efx_rc_t, rc);
127
128 return (rc);
129 }
130
131 __checkReturn efx_rc_t
ef10_vpd_read(__in efx_nic_t * enp,__out_bcount (size)caddr_t data,__in size_t size)132 ef10_vpd_read(
133 __in efx_nic_t *enp,
134 __out_bcount(size) caddr_t data,
135 __in size_t size)
136 {
137 caddr_t dvpd;
138 size_t dvpd_size;
139 uint32_t pci_pf;
140 uint32_t tag;
141 efx_rc_t rc;
142
143 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
144 enp->en_family == EFX_FAMILY_MEDFORD ||
145 enp->en_family == EFX_FAMILY_MEDFORD2);
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 if (dvpd != NULL)
164 memcpy(data, dvpd, dvpd_size);
165
166 /* Pad data with all-1s, consistent with update operations */
167 memset(data + dvpd_size, 0xff, size - dvpd_size);
168
169 if (dvpd != NULL)
170 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
171
172 return (0);
173
174 fail2:
175 EFSYS_PROBE(fail2);
176
177 if (dvpd != NULL)
178 EFSYS_KMEM_FREE(enp->en_esip, dvpd_size, dvpd);
179 fail1:
180 EFSYS_PROBE1(fail1, efx_rc_t, rc);
181
182 return (rc);
183 }
184
185 __checkReturn efx_rc_t
ef10_vpd_verify(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)186 ef10_vpd_verify(
187 __in efx_nic_t *enp,
188 __in_bcount(size) caddr_t data,
189 __in size_t size)
190 {
191 efx_vpd_tag_t stag;
192 efx_vpd_tag_t dtag;
193 efx_vpd_keyword_t skey;
194 efx_vpd_keyword_t dkey;
195 unsigned int scont;
196 unsigned int dcont;
197 efx_rc_t rc;
198
199 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
200 enp->en_family == EFX_FAMILY_MEDFORD ||
201 enp->en_family == EFX_FAMILY_MEDFORD2);
202
203 /*
204 * Strictly you could take the view that dynamic vpd is optional.
205 * Instead, to conform more closely to the read/verify/reinit()
206 * paradigm, we require dynamic vpd. ef10_vpd_reinit() will
207 * reinitialize it as required.
208 */
209 if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
210 goto fail1;
211
212 /*
213 * Verify that there is no duplication between the static and
214 * dynamic cfg sectors.
215 */
216 if (enp->en_arch.ef10.ena_svpd_length == 0)
217 goto done;
218
219 dcont = 0;
220 _NOTE(CONSTANTCONDITION)
221 while (1) {
222 if ((rc = efx_vpd_hunk_next(data, size, &dtag,
223 &dkey, NULL, NULL, &dcont)) != 0)
224 goto fail2;
225 if (dcont == 0)
226 break;
227
228 /*
229 * Skip the RV keyword. It should be present in both the static
230 * and dynamic cfg sectors.
231 */
232 if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
233 continue;
234
235 scont = 0;
236 _NOTE(CONSTANTCONDITION)
237 while (1) {
238 if ((rc = efx_vpd_hunk_next(
239 enp->en_arch.ef10.ena_svpd,
240 enp->en_arch.ef10.ena_svpd_length, &stag, &skey,
241 NULL, NULL, &scont)) != 0)
242 goto fail3;
243 if (scont == 0)
244 break;
245
246 if (stag == dtag && skey == dkey) {
247 rc = EEXIST;
248 goto fail4;
249 }
250 }
251 }
252
253 done:
254 return (0);
255
256 fail4:
257 EFSYS_PROBE(fail4);
258 fail3:
259 EFSYS_PROBE(fail3);
260 fail2:
261 EFSYS_PROBE(fail2);
262 fail1:
263 EFSYS_PROBE1(fail1, efx_rc_t, rc);
264
265 return (rc);
266 }
267
268 __checkReturn efx_rc_t
ef10_vpd_reinit(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)269 ef10_vpd_reinit(
270 __in efx_nic_t *enp,
271 __in_bcount(size) caddr_t data,
272 __in size_t size)
273 {
274 boolean_t wantpid;
275 efx_rc_t rc;
276
277 /*
278 * Only create an ID string if the dynamic cfg doesn't have one
279 */
280 if (enp->en_arch.ef10.ena_svpd_length == 0)
281 wantpid = B_TRUE;
282 else {
283 unsigned int offset;
284 uint8_t length;
285
286 rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
287 enp->en_arch.ef10.ena_svpd_length,
288 EFX_VPD_ID, 0, &offset, &length);
289 if (rc == 0)
290 wantpid = B_FALSE;
291 else if (rc == ENOENT)
292 wantpid = B_TRUE;
293 else
294 goto fail1;
295 }
296
297 if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
298 goto fail2;
299
300 return (0);
301
302 fail2:
303 EFSYS_PROBE(fail2);
304 fail1:
305 EFSYS_PROBE1(fail1, efx_rc_t, rc);
306
307 return (rc);
308 }
309
310 __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)311 ef10_vpd_get(
312 __in efx_nic_t *enp,
313 __in_bcount(size) caddr_t data,
314 __in size_t size,
315 __inout efx_vpd_value_t *evvp)
316 {
317 unsigned int offset;
318 uint8_t length;
319 efx_rc_t rc;
320
321 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
322 enp->en_family == EFX_FAMILY_MEDFORD ||
323 enp->en_family == EFX_FAMILY_MEDFORD2);
324
325 /* Attempt to satisfy the request from svpd first */
326 if (enp->en_arch.ef10.ena_svpd_length > 0) {
327 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
328 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
329 evvp->evv_keyword, &offset, &length)) == 0) {
330 evvp->evv_length = length;
331 memcpy(evvp->evv_value,
332 enp->en_arch.ef10.ena_svpd + offset, length);
333 return (0);
334 } else if (rc != ENOENT)
335 goto fail1;
336 }
337
338 /* And then from the provided data buffer */
339 if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
340 evvp->evv_keyword, &offset, &length)) != 0) {
341 if (rc == ENOENT)
342 return (rc);
343 goto fail2;
344 }
345
346 evvp->evv_length = length;
347 memcpy(evvp->evv_value, data + offset, length);
348
349 return (0);
350
351 fail2:
352 EFSYS_PROBE(fail2);
353 fail1:
354 EFSYS_PROBE1(fail1, efx_rc_t, rc);
355
356 return (rc);
357 }
358
359 __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)360 ef10_vpd_set(
361 __in efx_nic_t *enp,
362 __in_bcount(size) caddr_t data,
363 __in size_t size,
364 __in efx_vpd_value_t *evvp)
365 {
366 efx_rc_t rc;
367
368 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
369 enp->en_family == EFX_FAMILY_MEDFORD ||
370 enp->en_family == EFX_FAMILY_MEDFORD2);
371
372 /* If the provided (tag,keyword) exists in svpd, then it is readonly */
373 if (enp->en_arch.ef10.ena_svpd_length > 0) {
374 unsigned int offset;
375 uint8_t length;
376
377 if ((rc = efx_vpd_hunk_get(enp->en_arch.ef10.ena_svpd,
378 enp->en_arch.ef10.ena_svpd_length, evvp->evv_tag,
379 evvp->evv_keyword, &offset, &length)) == 0) {
380 rc = EACCES;
381 goto fail1;
382 }
383 }
384
385 if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
386 goto fail2;
387
388 return (0);
389
390 fail2:
391 EFSYS_PROBE(fail2);
392 fail1:
393 EFSYS_PROBE1(fail1, efx_rc_t, rc);
394
395 return (rc);
396 }
397
398 __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)399 ef10_vpd_next(
400 __in efx_nic_t *enp,
401 __in_bcount(size) caddr_t data,
402 __in size_t size,
403 __out efx_vpd_value_t *evvp,
404 __inout unsigned int *contp)
405 {
406 _NOTE(ARGUNUSED(enp, data, size, evvp, contp))
407
408 return (ENOTSUP);
409 }
410
411 __checkReturn efx_rc_t
ef10_vpd_write(__in efx_nic_t * enp,__in_bcount (size)caddr_t data,__in size_t size)412 ef10_vpd_write(
413 __in efx_nic_t *enp,
414 __in_bcount(size) caddr_t data,
415 __in size_t size)
416 {
417 size_t vpd_length;
418 uint32_t pci_pf;
419 uint32_t tag;
420 efx_rc_t rc;
421
422 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
423 enp->en_family == EFX_FAMILY_MEDFORD ||
424 enp->en_family == EFX_FAMILY_MEDFORD2);
425
426 if (enp->en_nic_cfg.enc_vpd_is_global) {
427 tag = TLV_TAG_GLOBAL_DYNAMIC_VPD;
428 } else {
429 pci_pf = enp->en_nic_cfg.enc_pf;
430 tag = TLV_TAG_PF_DYNAMIC_VPD(pci_pf);
431 }
432
433 /* Determine total length of new dynamic VPD */
434 if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
435 goto fail1;
436
437 /* Store new dynamic VPD in all segments in DYNAMIC_CONFIG partition */
438 if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
439 NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
440 tag, data, vpd_length, B_TRUE)) != 0) {
441 goto fail2;
442 }
443
444 return (0);
445
446 fail2:
447 EFSYS_PROBE(fail2);
448
449 fail1:
450 EFSYS_PROBE1(fail1, efx_rc_t, rc);
451
452 return (rc);
453 }
454
455 void
ef10_vpd_fini(__in efx_nic_t * enp)456 ef10_vpd_fini(
457 __in efx_nic_t *enp)
458 {
459 EFSYS_ASSERT(enp->en_family == EFX_FAMILY_HUNTINGTON ||
460 enp->en_family == EFX_FAMILY_MEDFORD ||
461 enp->en_family == EFX_FAMILY_MEDFORD2);
462
463 if (enp->en_arch.ef10.ena_svpd_length > 0) {
464 EFSYS_KMEM_FREE(enp->en_esip, enp->en_arch.ef10.ena_svpd_length,
465 enp->en_arch.ef10.ena_svpd);
466
467 enp->en_arch.ef10.ena_svpd = NULL;
468 enp->en_arch.ef10.ena_svpd_length = 0;
469 }
470 }
471
472 #endif /* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
473
474 #endif /* EFSYS_OPT_VPD */
475