]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * | |
3 | * Copyright (c) 2009-2018 Solarflare Communications Inc. | |
4 | * All rights reserved. | |
5 | */ | |
6 | ||
7 | #include "efx.h" | |
8 | #include "efx_impl.h" | |
9 | ||
10 | #if EFSYS_OPT_VPD | |
11 | ||
12 | #define TAG_TYPE_LBN 7 | |
13 | #define TAG_TYPE_WIDTH 1 | |
14 | #define TAG_TYPE_LARGE_ITEM_DECODE 1 | |
15 | #define TAG_TYPE_SMALL_ITEM_DECODE 0 | |
16 | ||
17 | #define TAG_SMALL_ITEM_NAME_LBN 3 | |
18 | #define TAG_SMALL_ITEM_NAME_WIDTH 4 | |
19 | #define TAG_SMALL_ITEM_SIZE_LBN 0 | |
20 | #define TAG_SMALL_ITEM_SIZE_WIDTH 3 | |
21 | ||
22 | #define TAG_LARGE_ITEM_NAME_LBN 0 | |
23 | #define TAG_LARGE_ITEM_NAME_WIDTH 7 | |
24 | ||
25 | #define TAG_NAME_END_DECODE 0x0f | |
26 | #define TAG_NAME_ID_STRING_DECODE 0x02 | |
27 | #define TAG_NAME_VPD_R_DECODE 0x10 | |
28 | #define TAG_NAME_VPD_W_DECODE 0x11 | |
29 | ||
30 | #if EFSYS_OPT_SIENA | |
31 | ||
32 | static const efx_vpd_ops_t __efx_vpd_siena_ops = { | |
33 | siena_vpd_init, /* evpdo_init */ | |
34 | siena_vpd_size, /* evpdo_size */ | |
35 | siena_vpd_read, /* evpdo_read */ | |
36 | siena_vpd_verify, /* evpdo_verify */ | |
37 | siena_vpd_reinit, /* evpdo_reinit */ | |
38 | siena_vpd_get, /* evpdo_get */ | |
39 | siena_vpd_set, /* evpdo_set */ | |
40 | siena_vpd_next, /* evpdo_next */ | |
41 | siena_vpd_write, /* evpdo_write */ | |
42 | siena_vpd_fini, /* evpdo_fini */ | |
43 | }; | |
44 | ||
45 | #endif /* EFSYS_OPT_SIENA */ | |
46 | ||
9f95a23c | 47 | #if EFX_OPTS_EF10() |
11fdf7f2 TL |
48 | |
49 | static const efx_vpd_ops_t __efx_vpd_ef10_ops = { | |
50 | ef10_vpd_init, /* evpdo_init */ | |
51 | ef10_vpd_size, /* evpdo_size */ | |
52 | ef10_vpd_read, /* evpdo_read */ | |
53 | ef10_vpd_verify, /* evpdo_verify */ | |
54 | ef10_vpd_reinit, /* evpdo_reinit */ | |
55 | ef10_vpd_get, /* evpdo_get */ | |
56 | ef10_vpd_set, /* evpdo_set */ | |
57 | ef10_vpd_next, /* evpdo_next */ | |
58 | ef10_vpd_write, /* evpdo_write */ | |
59 | ef10_vpd_fini, /* evpdo_fini */ | |
60 | }; | |
61 | ||
9f95a23c | 62 | #endif /* EFX_OPTS_EF10() */ |
11fdf7f2 TL |
63 | |
64 | __checkReturn efx_rc_t | |
65 | efx_vpd_init( | |
66 | __in efx_nic_t *enp) | |
67 | { | |
68 | const efx_vpd_ops_t *evpdop; | |
69 | efx_rc_t rc; | |
70 | ||
71 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
72 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); | |
73 | EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_VPD)); | |
74 | ||
75 | switch (enp->en_family) { | |
76 | #if EFSYS_OPT_SIENA | |
77 | case EFX_FAMILY_SIENA: | |
78 | evpdop = &__efx_vpd_siena_ops; | |
79 | break; | |
80 | #endif /* EFSYS_OPT_SIENA */ | |
81 | ||
82 | #if EFSYS_OPT_HUNTINGTON | |
83 | case EFX_FAMILY_HUNTINGTON: | |
84 | evpdop = &__efx_vpd_ef10_ops; | |
85 | break; | |
86 | #endif /* EFSYS_OPT_HUNTINGTON */ | |
87 | ||
88 | #if EFSYS_OPT_MEDFORD | |
89 | case EFX_FAMILY_MEDFORD: | |
90 | evpdop = &__efx_vpd_ef10_ops; | |
91 | break; | |
92 | #endif /* EFSYS_OPT_MEDFORD */ | |
93 | ||
94 | #if EFSYS_OPT_MEDFORD2 | |
95 | case EFX_FAMILY_MEDFORD2: | |
96 | evpdop = &__efx_vpd_ef10_ops; | |
97 | break; | |
98 | #endif /* EFSYS_OPT_MEDFORD2 */ | |
99 | ||
100 | default: | |
101 | EFSYS_ASSERT(0); | |
102 | rc = ENOTSUP; | |
103 | goto fail1; | |
104 | } | |
105 | ||
106 | if (evpdop->evpdo_init != NULL) { | |
107 | if ((rc = evpdop->evpdo_init(enp)) != 0) | |
108 | goto fail2; | |
109 | } | |
110 | ||
111 | enp->en_evpdop = evpdop; | |
112 | enp->en_mod_flags |= EFX_MOD_VPD; | |
113 | ||
114 | return (0); | |
115 | ||
116 | fail2: | |
117 | EFSYS_PROBE(fail2); | |
118 | fail1: | |
119 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
120 | ||
121 | return (rc); | |
122 | } | |
123 | ||
124 | __checkReturn efx_rc_t | |
125 | efx_vpd_size( | |
126 | __in efx_nic_t *enp, | |
127 | __out size_t *sizep) | |
128 | { | |
129 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
130 | efx_rc_t rc; | |
131 | ||
132 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
133 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
134 | ||
135 | if ((rc = evpdop->evpdo_size(enp, sizep)) != 0) | |
136 | goto fail1; | |
137 | ||
138 | return (0); | |
139 | ||
140 | fail1: | |
141 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
142 | ||
143 | return (rc); | |
144 | } | |
145 | ||
146 | __checkReturn efx_rc_t | |
147 | efx_vpd_read( | |
148 | __in efx_nic_t *enp, | |
149 | __out_bcount(size) caddr_t data, | |
150 | __in size_t size) | |
151 | { | |
152 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
153 | efx_rc_t rc; | |
154 | ||
155 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
156 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
157 | ||
158 | if ((rc = evpdop->evpdo_read(enp, data, size)) != 0) | |
159 | goto fail1; | |
160 | ||
161 | return (0); | |
162 | ||
163 | fail1: | |
164 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
165 | ||
166 | return (rc); | |
167 | } | |
168 | ||
169 | __checkReturn efx_rc_t | |
170 | efx_vpd_verify( | |
171 | __in efx_nic_t *enp, | |
172 | __in_bcount(size) caddr_t data, | |
173 | __in size_t size) | |
174 | { | |
175 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
176 | efx_rc_t rc; | |
177 | ||
178 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
179 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
180 | ||
181 | if ((rc = evpdop->evpdo_verify(enp, data, size)) != 0) | |
182 | goto fail1; | |
183 | ||
184 | return (0); | |
185 | ||
186 | fail1: | |
187 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
188 | ||
189 | return (rc); | |
190 | } | |
191 | ||
192 | __checkReturn efx_rc_t | |
193 | efx_vpd_reinit( | |
194 | __in efx_nic_t *enp, | |
195 | __in_bcount(size) caddr_t data, | |
196 | __in size_t size) | |
197 | { | |
198 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
199 | efx_rc_t rc; | |
200 | ||
201 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
202 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
203 | ||
204 | if (evpdop->evpdo_reinit == NULL) { | |
205 | rc = ENOTSUP; | |
206 | goto fail1; | |
207 | } | |
208 | ||
209 | if ((rc = evpdop->evpdo_reinit(enp, data, size)) != 0) | |
210 | goto fail2; | |
211 | ||
212 | return (0); | |
213 | ||
214 | fail2: | |
215 | EFSYS_PROBE(fail2); | |
216 | fail1: | |
217 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
218 | ||
219 | return (rc); | |
220 | } | |
221 | ||
222 | __checkReturn efx_rc_t | |
223 | efx_vpd_get( | |
224 | __in efx_nic_t *enp, | |
225 | __in_bcount(size) caddr_t data, | |
226 | __in size_t size, | |
227 | __inout efx_vpd_value_t *evvp) | |
228 | { | |
229 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
230 | efx_rc_t rc; | |
231 | ||
232 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
233 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
234 | ||
235 | if ((rc = evpdop->evpdo_get(enp, data, size, evvp)) != 0) { | |
236 | if (rc == ENOENT) | |
237 | return (rc); | |
238 | ||
239 | goto fail1; | |
240 | } | |
241 | ||
242 | return (0); | |
243 | ||
244 | fail1: | |
245 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
246 | ||
247 | return (rc); | |
248 | } | |
249 | ||
250 | __checkReturn efx_rc_t | |
251 | efx_vpd_set( | |
252 | __in efx_nic_t *enp, | |
253 | __inout_bcount(size) caddr_t data, | |
254 | __in size_t size, | |
255 | __in efx_vpd_value_t *evvp) | |
256 | { | |
257 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
258 | efx_rc_t rc; | |
259 | ||
260 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
261 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
262 | ||
263 | if ((rc = evpdop->evpdo_set(enp, data, size, evvp)) != 0) | |
264 | goto fail1; | |
265 | ||
266 | return (0); | |
267 | ||
268 | fail1: | |
269 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
270 | ||
271 | return (rc); | |
272 | } | |
273 | ||
274 | __checkReturn efx_rc_t | |
275 | efx_vpd_next( | |
276 | __in efx_nic_t *enp, | |
277 | __inout_bcount(size) caddr_t data, | |
278 | __in size_t size, | |
279 | __out efx_vpd_value_t *evvp, | |
280 | __inout unsigned int *contp) | |
281 | { | |
282 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
283 | efx_rc_t rc; | |
284 | ||
285 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
286 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
287 | ||
288 | if ((rc = evpdop->evpdo_next(enp, data, size, evvp, contp)) != 0) | |
289 | goto fail1; | |
290 | ||
291 | return (0); | |
292 | ||
293 | fail1: | |
294 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
295 | ||
296 | return (rc); | |
297 | } | |
298 | ||
299 | __checkReturn efx_rc_t | |
300 | efx_vpd_write( | |
301 | __in efx_nic_t *enp, | |
302 | __in_bcount(size) caddr_t data, | |
303 | __in size_t size) | |
304 | { | |
305 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
306 | efx_rc_t rc; | |
307 | ||
308 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
309 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
310 | ||
311 | if ((rc = evpdop->evpdo_write(enp, data, size)) != 0) | |
312 | goto fail1; | |
313 | ||
314 | return (0); | |
315 | ||
316 | fail1: | |
317 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
318 | ||
319 | return (rc); | |
320 | } | |
321 | ||
322 | static __checkReturn efx_rc_t | |
323 | efx_vpd_next_tag( | |
324 | __in caddr_t data, | |
325 | __in size_t size, | |
326 | __inout unsigned int *offsetp, | |
327 | __out efx_vpd_tag_t *tagp, | |
328 | __out uint16_t *lengthp) | |
329 | { | |
330 | efx_byte_t byte; | |
331 | efx_word_t word; | |
332 | uint8_t name; | |
333 | uint16_t length; | |
334 | size_t headlen; | |
335 | efx_rc_t rc; | |
336 | ||
337 | if (*offsetp >= size) { | |
338 | rc = EFAULT; | |
339 | goto fail1; | |
340 | } | |
341 | ||
342 | EFX_POPULATE_BYTE_1(byte, EFX_BYTE_0, data[*offsetp]); | |
343 | ||
344 | switch (EFX_BYTE_FIELD(byte, TAG_TYPE)) { | |
345 | case TAG_TYPE_SMALL_ITEM_DECODE: | |
346 | headlen = 1; | |
347 | ||
348 | name = EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_NAME); | |
349 | length = (uint16_t)EFX_BYTE_FIELD(byte, TAG_SMALL_ITEM_SIZE); | |
350 | ||
351 | break; | |
352 | ||
353 | case TAG_TYPE_LARGE_ITEM_DECODE: | |
354 | headlen = 3; | |
355 | ||
356 | if (*offsetp + headlen > size) { | |
357 | rc = EFAULT; | |
358 | goto fail2; | |
359 | } | |
360 | ||
361 | name = EFX_BYTE_FIELD(byte, TAG_LARGE_ITEM_NAME); | |
362 | EFX_POPULATE_WORD_2(word, | |
363 | EFX_BYTE_0, data[*offsetp + 1], | |
364 | EFX_BYTE_1, data[*offsetp + 2]); | |
365 | length = EFX_WORD_FIELD(word, EFX_WORD_0); | |
366 | ||
367 | break; | |
368 | ||
369 | default: | |
370 | rc = EFAULT; | |
371 | goto fail2; | |
372 | } | |
373 | ||
374 | if (*offsetp + headlen + length > size) { | |
375 | rc = EFAULT; | |
376 | goto fail3; | |
377 | } | |
378 | ||
379 | EFX_STATIC_ASSERT(TAG_NAME_END_DECODE == EFX_VPD_END); | |
380 | EFX_STATIC_ASSERT(TAG_NAME_ID_STRING_DECODE == EFX_VPD_ID); | |
381 | EFX_STATIC_ASSERT(TAG_NAME_VPD_R_DECODE == EFX_VPD_RO); | |
382 | EFX_STATIC_ASSERT(TAG_NAME_VPD_W_DECODE == EFX_VPD_RW); | |
383 | if (name != EFX_VPD_END && name != EFX_VPD_ID && | |
384 | name != EFX_VPD_RO) { | |
385 | rc = EFAULT; | |
386 | goto fail4; | |
387 | } | |
388 | ||
389 | *tagp = name; | |
390 | *lengthp = length; | |
391 | *offsetp += headlen; | |
392 | ||
393 | return (0); | |
394 | ||
395 | fail4: | |
396 | EFSYS_PROBE(fail4); | |
397 | fail3: | |
398 | EFSYS_PROBE(fail3); | |
399 | fail2: | |
400 | EFSYS_PROBE(fail2); | |
401 | fail1: | |
402 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
403 | ||
404 | return (rc); | |
405 | } | |
406 | ||
407 | static __checkReturn efx_rc_t | |
408 | efx_vpd_next_keyword( | |
409 | __in_bcount(size) caddr_t tag, | |
410 | __in size_t size, | |
411 | __in unsigned int pos, | |
412 | __out efx_vpd_keyword_t *keywordp, | |
413 | __out uint8_t *lengthp) | |
414 | { | |
415 | efx_vpd_keyword_t keyword; | |
416 | uint8_t length; | |
417 | efx_rc_t rc; | |
418 | ||
419 | if (pos + 3U > size) { | |
420 | rc = EFAULT; | |
421 | goto fail1; | |
422 | } | |
423 | ||
424 | keyword = EFX_VPD_KEYWORD(tag[pos], tag[pos + 1]); | |
425 | length = tag[pos + 2]; | |
426 | ||
427 | if (length == 0 || pos + 3U + length > size) { | |
428 | rc = EFAULT; | |
429 | goto fail2; | |
430 | } | |
431 | ||
432 | *keywordp = keyword; | |
433 | *lengthp = length; | |
434 | ||
435 | return (0); | |
436 | ||
437 | fail2: | |
438 | EFSYS_PROBE(fail2); | |
439 | fail1: | |
440 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
441 | ||
442 | return (rc); | |
443 | } | |
444 | ||
445 | __checkReturn efx_rc_t | |
446 | efx_vpd_hunk_length( | |
447 | __in_bcount(size) caddr_t data, | |
448 | __in size_t size, | |
449 | __out size_t *lengthp) | |
450 | { | |
451 | efx_vpd_tag_t tag; | |
452 | unsigned int offset; | |
453 | uint16_t taglen; | |
454 | efx_rc_t rc; | |
455 | ||
456 | offset = 0; | |
457 | _NOTE(CONSTANTCONDITION) | |
458 | while (1) { | |
459 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
460 | &tag, &taglen)) != 0) | |
461 | goto fail1; | |
462 | offset += taglen; | |
463 | if (tag == EFX_VPD_END) | |
464 | break; | |
465 | } | |
466 | ||
467 | *lengthp = offset; | |
468 | ||
469 | return (0); | |
470 | ||
471 | fail1: | |
472 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
473 | ||
474 | return (rc); | |
475 | } | |
476 | ||
477 | __checkReturn efx_rc_t | |
478 | efx_vpd_hunk_verify( | |
479 | __in_bcount(size) caddr_t data, | |
480 | __in size_t size, | |
481 | __out_opt boolean_t *cksummedp) | |
482 | { | |
483 | efx_vpd_tag_t tag; | |
484 | efx_vpd_keyword_t keyword; | |
485 | unsigned int offset; | |
486 | unsigned int pos; | |
487 | unsigned int i; | |
488 | uint16_t taglen; | |
489 | uint8_t keylen; | |
490 | uint8_t cksum; | |
491 | boolean_t cksummed = B_FALSE; | |
492 | efx_rc_t rc; | |
493 | ||
494 | /* | |
495 | * Parse every tag,keyword in the existing VPD. If the csum is present, | |
496 | * the assert it is correct, and is the final keyword in the RO block. | |
497 | */ | |
498 | offset = 0; | |
499 | _NOTE(CONSTANTCONDITION) | |
500 | while (1) { | |
501 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
502 | &tag, &taglen)) != 0) | |
503 | goto fail1; | |
504 | if (tag == EFX_VPD_END) | |
505 | break; | |
506 | else if (tag == EFX_VPD_ID) | |
507 | goto done; | |
508 | ||
509 | for (pos = 0; pos != taglen; pos += 3 + keylen) { | |
510 | /* RV keyword must be the last in the block */ | |
511 | if (cksummed) { | |
512 | rc = EFAULT; | |
513 | goto fail2; | |
514 | } | |
515 | ||
516 | if ((rc = efx_vpd_next_keyword(data + offset, | |
517 | taglen, pos, &keyword, &keylen)) != 0) | |
518 | goto fail3; | |
519 | ||
520 | if (keyword == EFX_VPD_KEYWORD('R', 'V')) { | |
521 | cksum = 0; | |
522 | for (i = 0; i < offset + pos + 4; i++) | |
523 | cksum += data[i]; | |
524 | ||
525 | if (cksum != 0) { | |
526 | rc = EFAULT; | |
527 | goto fail4; | |
528 | } | |
529 | ||
530 | cksummed = B_TRUE; | |
531 | } | |
532 | } | |
533 | ||
534 | done: | |
535 | offset += taglen; | |
536 | } | |
537 | ||
538 | if (!cksummed) { | |
539 | rc = EFAULT; | |
540 | goto fail5; | |
541 | } | |
542 | ||
543 | if (cksummedp != NULL) | |
544 | *cksummedp = cksummed; | |
545 | ||
546 | return (0); | |
547 | ||
548 | fail5: | |
549 | EFSYS_PROBE(fail5); | |
550 | fail4: | |
551 | EFSYS_PROBE(fail4); | |
552 | fail3: | |
553 | EFSYS_PROBE(fail3); | |
554 | fail2: | |
555 | EFSYS_PROBE(fail2); | |
556 | fail1: | |
557 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
558 | ||
559 | return (rc); | |
560 | } | |
561 | ||
562 | static uint8_t __efx_vpd_blank_pid[] = { | |
563 | /* Large resource type ID length 1 */ | |
564 | 0x82, 0x01, 0x00, | |
565 | /* Product name ' ' */ | |
566 | 0x32, | |
567 | }; | |
568 | ||
569 | static uint8_t __efx_vpd_blank_r[] = { | |
570 | /* Large resource type VPD-R length 4 */ | |
571 | 0x90, 0x04, 0x00, | |
572 | /* RV keyword length 1 */ | |
573 | 'R', 'V', 0x01, | |
574 | /* RV payload checksum */ | |
575 | 0x00, | |
576 | }; | |
577 | ||
578 | __checkReturn efx_rc_t | |
579 | efx_vpd_hunk_reinit( | |
580 | __in_bcount(size) caddr_t data, | |
581 | __in size_t size, | |
582 | __in boolean_t wantpid) | |
583 | { | |
584 | unsigned int offset = 0; | |
585 | unsigned int pos; | |
586 | efx_byte_t byte; | |
587 | uint8_t cksum; | |
588 | efx_rc_t rc; | |
589 | ||
590 | if (size < 0x100) { | |
591 | rc = ENOSPC; | |
592 | goto fail1; | |
593 | } | |
594 | ||
595 | if (wantpid) { | |
596 | memcpy(data + offset, __efx_vpd_blank_pid, | |
597 | sizeof (__efx_vpd_blank_pid)); | |
598 | offset += sizeof (__efx_vpd_blank_pid); | |
599 | } | |
600 | ||
601 | memcpy(data + offset, __efx_vpd_blank_r, sizeof (__efx_vpd_blank_r)); | |
602 | offset += sizeof (__efx_vpd_blank_r); | |
603 | ||
604 | /* Update checksum */ | |
605 | cksum = 0; | |
606 | for (pos = 0; pos < offset; pos++) | |
607 | cksum += data[pos]; | |
608 | data[offset - 1] -= cksum; | |
609 | ||
610 | /* Append trailing tag */ | |
611 | EFX_POPULATE_BYTE_3(byte, | |
612 | TAG_TYPE, TAG_TYPE_SMALL_ITEM_DECODE, | |
613 | TAG_SMALL_ITEM_NAME, TAG_NAME_END_DECODE, | |
614 | TAG_SMALL_ITEM_SIZE, 0); | |
615 | data[offset] = EFX_BYTE_FIELD(byte, EFX_BYTE_0); | |
616 | offset++; | |
617 | ||
618 | return (0); | |
619 | ||
620 | fail1: | |
621 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
622 | ||
623 | return (rc); | |
624 | } | |
625 | ||
626 | __checkReturn efx_rc_t | |
627 | efx_vpd_hunk_next( | |
628 | __in_bcount(size) caddr_t data, | |
629 | __in size_t size, | |
630 | __out efx_vpd_tag_t *tagp, | |
631 | __out efx_vpd_keyword_t *keywordp, | |
632 | __out_opt unsigned int *payloadp, | |
633 | __out_opt uint8_t *paylenp, | |
634 | __inout unsigned int *contp) | |
635 | { | |
636 | efx_vpd_tag_t tag; | |
637 | efx_vpd_keyword_t keyword = 0; | |
638 | unsigned int offset; | |
639 | unsigned int pos; | |
640 | unsigned int index; | |
641 | uint16_t taglen; | |
642 | uint8_t keylen; | |
643 | uint8_t paylen; | |
644 | efx_rc_t rc; | |
645 | ||
646 | offset = index = 0; | |
647 | _NOTE(CONSTANTCONDITION) | |
648 | while (1) { | |
649 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
650 | &tag, &taglen)) != 0) | |
651 | goto fail1; | |
652 | ||
653 | if (tag == EFX_VPD_END) { | |
654 | keyword = 0; | |
655 | paylen = 0; | |
656 | index = 0; | |
657 | break; | |
658 | } | |
659 | ||
660 | if (tag == EFX_VPD_ID) { | |
661 | if (index++ == *contp) { | |
662 | EFSYS_ASSERT3U(taglen, <, 0x100); | |
663 | keyword = 0; | |
664 | paylen = (uint8_t)MIN(taglen, 0xff); | |
665 | ||
666 | goto done; | |
667 | } | |
668 | } else { | |
669 | for (pos = 0; pos != taglen; pos += 3 + keylen) { | |
670 | if ((rc = efx_vpd_next_keyword(data + offset, | |
671 | taglen, pos, &keyword, &keylen)) != 0) | |
672 | goto fail2; | |
673 | ||
674 | if (index++ == *contp) { | |
675 | offset += pos + 3; | |
676 | paylen = keylen; | |
677 | ||
678 | goto done; | |
679 | } | |
680 | } | |
681 | } | |
682 | ||
683 | offset += taglen; | |
684 | } | |
685 | ||
686 | done: | |
687 | *tagp = tag; | |
688 | *keywordp = keyword; | |
689 | if (payloadp != NULL) | |
690 | *payloadp = offset; | |
691 | if (paylenp != NULL) | |
692 | *paylenp = paylen; | |
693 | ||
694 | *contp = index; | |
695 | return (0); | |
696 | ||
697 | fail2: | |
698 | EFSYS_PROBE(fail2); | |
699 | fail1: | |
700 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
701 | ||
702 | return (rc); | |
703 | } | |
704 | ||
705 | __checkReturn efx_rc_t | |
706 | efx_vpd_hunk_get( | |
707 | __in_bcount(size) caddr_t data, | |
708 | __in size_t size, | |
709 | __in efx_vpd_tag_t tag, | |
710 | __in efx_vpd_keyword_t keyword, | |
711 | __out unsigned int *payloadp, | |
712 | __out uint8_t *paylenp) | |
713 | { | |
714 | efx_vpd_tag_t itag; | |
715 | efx_vpd_keyword_t ikeyword; | |
716 | unsigned int offset; | |
717 | unsigned int pos; | |
718 | uint16_t taglen; | |
719 | uint8_t keylen; | |
720 | efx_rc_t rc; | |
721 | ||
722 | offset = 0; | |
723 | _NOTE(CONSTANTCONDITION) | |
724 | while (1) { | |
725 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
726 | &itag, &taglen)) != 0) | |
727 | goto fail1; | |
728 | if (itag == EFX_VPD_END) | |
729 | break; | |
730 | ||
731 | if (itag == tag) { | |
732 | if (itag == EFX_VPD_ID) { | |
733 | EFSYS_ASSERT3U(taglen, <, 0x100); | |
734 | ||
735 | *paylenp = (uint8_t)MIN(taglen, 0xff); | |
736 | *payloadp = offset; | |
737 | return (0); | |
738 | } | |
739 | ||
740 | for (pos = 0; pos != taglen; pos += 3 + keylen) { | |
741 | if ((rc = efx_vpd_next_keyword(data + offset, | |
742 | taglen, pos, &ikeyword, &keylen)) != 0) | |
743 | goto fail2; | |
744 | ||
745 | if (ikeyword == keyword) { | |
746 | *paylenp = keylen; | |
747 | *payloadp = offset + pos + 3; | |
748 | return (0); | |
749 | } | |
750 | } | |
751 | } | |
752 | ||
753 | offset += taglen; | |
754 | } | |
755 | ||
756 | /* Not an error */ | |
757 | return (ENOENT); | |
758 | ||
759 | fail2: | |
760 | EFSYS_PROBE(fail2); | |
761 | fail1: | |
762 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
763 | ||
764 | return (rc); | |
765 | } | |
766 | ||
767 | __checkReturn efx_rc_t | |
768 | efx_vpd_hunk_set( | |
769 | __in_bcount(size) caddr_t data, | |
770 | __in size_t size, | |
771 | __in efx_vpd_value_t *evvp) | |
772 | { | |
773 | efx_word_t word; | |
774 | efx_vpd_tag_t tag; | |
775 | efx_vpd_keyword_t keyword; | |
776 | unsigned int offset; | |
777 | unsigned int pos; | |
778 | unsigned int taghead; | |
779 | unsigned int source; | |
780 | unsigned int dest; | |
781 | unsigned int i; | |
782 | uint16_t taglen; | |
783 | uint8_t keylen; | |
784 | uint8_t cksum; | |
785 | size_t used; | |
786 | efx_rc_t rc; | |
787 | ||
788 | switch (evvp->evv_tag) { | |
789 | case EFX_VPD_ID: | |
790 | if (evvp->evv_keyword != 0) { | |
791 | rc = EINVAL; | |
792 | goto fail1; | |
793 | } | |
794 | ||
795 | /* Can't delete the ID keyword */ | |
796 | if (evvp->evv_length == 0) { | |
797 | rc = EINVAL; | |
798 | goto fail1; | |
799 | } | |
800 | break; | |
801 | ||
802 | case EFX_VPD_RO: | |
803 | if (evvp->evv_keyword == EFX_VPD_KEYWORD('R', 'V')) { | |
804 | rc = EINVAL; | |
805 | goto fail1; | |
806 | } | |
807 | break; | |
808 | ||
809 | default: | |
810 | rc = EINVAL; | |
811 | goto fail1; | |
812 | } | |
813 | ||
814 | /* Determine total size of all current tags */ | |
815 | if ((rc = efx_vpd_hunk_length(data, size, &used)) != 0) | |
816 | goto fail2; | |
817 | ||
818 | offset = 0; | |
819 | _NOTE(CONSTANTCONDITION) | |
820 | while (1) { | |
821 | taghead = offset; | |
822 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
823 | &tag, &taglen)) != 0) | |
824 | goto fail3; | |
825 | if (tag == EFX_VPD_END) | |
826 | break; | |
827 | else if (tag != evvp->evv_tag) { | |
828 | offset += taglen; | |
829 | continue; | |
830 | } | |
831 | ||
832 | /* We only support modifying large resource tags */ | |
833 | if (offset - taghead != 3) { | |
834 | rc = EINVAL; | |
835 | goto fail4; | |
836 | } | |
837 | ||
838 | /* | |
839 | * Work out the offset of the byte immediately after the | |
840 | * old (=source) and new (=dest) new keyword/tag | |
841 | */ | |
842 | pos = 0; | |
843 | if (tag == EFX_VPD_ID) { | |
844 | source = offset + taglen; | |
845 | dest = offset + evvp->evv_length; | |
846 | goto check_space; | |
847 | } | |
848 | ||
849 | EFSYS_ASSERT3U(tag, ==, EFX_VPD_RO); | |
850 | source = dest = 0; | |
851 | for (pos = 0; pos != taglen; pos += 3 + keylen) { | |
852 | if ((rc = efx_vpd_next_keyword(data + offset, | |
853 | taglen, pos, &keyword, &keylen)) != 0) | |
854 | goto fail5; | |
855 | ||
856 | if (keyword == evvp->evv_keyword && | |
857 | evvp->evv_length == 0) { | |
858 | /* Deleting this keyword */ | |
859 | source = offset + pos + 3 + keylen; | |
860 | dest = offset + pos; | |
861 | break; | |
862 | ||
863 | } else if (keyword == evvp->evv_keyword) { | |
864 | /* Adjusting this keyword */ | |
865 | source = offset + pos + 3 + keylen; | |
866 | dest = offset + pos + 3 + evvp->evv_length; | |
867 | break; | |
868 | ||
869 | } else if (keyword == EFX_VPD_KEYWORD('R', 'V')) { | |
870 | /* The RV keyword must be at the end */ | |
871 | EFSYS_ASSERT3U(pos + 3 + keylen, ==, taglen); | |
872 | ||
873 | /* | |
874 | * The keyword doesn't already exist. If the | |
875 | * user deleting a non-existant keyword then | |
876 | * this is a no-op. | |
877 | */ | |
878 | if (evvp->evv_length == 0) | |
879 | return (0); | |
880 | ||
881 | /* Insert this keyword before the RV keyword */ | |
882 | source = offset + pos; | |
883 | dest = offset + pos + 3 + evvp->evv_length; | |
884 | break; | |
885 | } | |
886 | } | |
887 | ||
888 | check_space: | |
889 | if (used + dest > size + source) { | |
890 | rc = ENOSPC; | |
891 | goto fail6; | |
892 | } | |
893 | ||
894 | /* Move trailing data */ | |
895 | (void) memmove(data + dest, data + source, used - source); | |
896 | ||
897 | /* Copy contents */ | |
898 | memcpy(data + dest - evvp->evv_length, evvp->evv_value, | |
899 | evvp->evv_length); | |
900 | ||
901 | /* Insert new keyword header if required */ | |
902 | if (tag != EFX_VPD_ID && evvp->evv_length > 0) { | |
903 | EFX_POPULATE_WORD_1(word, EFX_WORD_0, | |
904 | evvp->evv_keyword); | |
905 | data[offset + pos + 0] = | |
906 | EFX_WORD_FIELD(word, EFX_BYTE_0); | |
907 | data[offset + pos + 1] = | |
908 | EFX_WORD_FIELD(word, EFX_BYTE_1); | |
909 | data[offset + pos + 2] = evvp->evv_length; | |
910 | } | |
911 | ||
912 | /* Modify tag length (large resource type) */ | |
913 | taglen += (uint16_t)(dest - source); | |
914 | EFX_POPULATE_WORD_1(word, EFX_WORD_0, taglen); | |
915 | data[offset - 2] = EFX_WORD_FIELD(word, EFX_BYTE_0); | |
916 | data[offset - 1] = EFX_WORD_FIELD(word, EFX_BYTE_1); | |
917 | ||
918 | goto checksum; | |
919 | } | |
920 | ||
921 | /* Unable to find the matching tag */ | |
922 | rc = ENOENT; | |
923 | goto fail7; | |
924 | ||
925 | checksum: | |
926 | /* Find the RV tag, and update the checksum */ | |
927 | offset = 0; | |
928 | _NOTE(CONSTANTCONDITION) | |
929 | while (1) { | |
930 | if ((rc = efx_vpd_next_tag(data, size, &offset, | |
931 | &tag, &taglen)) != 0) | |
932 | goto fail8; | |
933 | if (tag == EFX_VPD_END) | |
934 | break; | |
935 | if (tag == EFX_VPD_RO) { | |
936 | for (pos = 0; pos != taglen; pos += 3 + keylen) { | |
937 | if ((rc = efx_vpd_next_keyword(data + offset, | |
938 | taglen, pos, &keyword, &keylen)) != 0) | |
939 | goto fail9; | |
940 | ||
941 | if (keyword == EFX_VPD_KEYWORD('R', 'V')) { | |
942 | cksum = 0; | |
943 | for (i = 0; i < offset + pos + 3; i++) | |
944 | cksum += data[i]; | |
945 | data[i] = -cksum; | |
946 | break; | |
947 | } | |
948 | } | |
949 | } | |
950 | ||
951 | offset += taglen; | |
952 | } | |
953 | ||
954 | /* Zero out the unused portion */ | |
955 | (void) memset(data + offset + taglen, 0xff, size - offset - taglen); | |
956 | ||
957 | return (0); | |
958 | ||
959 | fail9: | |
960 | EFSYS_PROBE(fail9); | |
961 | fail8: | |
962 | EFSYS_PROBE(fail8); | |
963 | fail7: | |
964 | EFSYS_PROBE(fail7); | |
965 | fail6: | |
966 | EFSYS_PROBE(fail6); | |
967 | fail5: | |
968 | EFSYS_PROBE(fail5); | |
969 | fail4: | |
970 | EFSYS_PROBE(fail4); | |
971 | fail3: | |
972 | EFSYS_PROBE(fail3); | |
973 | fail2: | |
974 | EFSYS_PROBE(fail2); | |
975 | fail1: | |
976 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
977 | ||
978 | return (rc); | |
979 | } | |
980 | ||
981 | void | |
982 | efx_vpd_fini( | |
983 | __in efx_nic_t *enp) | |
984 | { | |
985 | const efx_vpd_ops_t *evpdop = enp->en_evpdop; | |
986 | ||
987 | EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); | |
988 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); | |
989 | EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_VPD); | |
990 | ||
991 | if (evpdop->evpdo_fini != NULL) | |
992 | evpdop->evpdo_fini(enp); | |
993 | ||
994 | enp->en_evpdop = NULL; | |
995 | enp->en_mod_flags &= ~EFX_MOD_VPD; | |
996 | } | |
997 | ||
998 | #endif /* EFSYS_OPT_VPD */ |