xref: /NextBSD/sys/dev/sfxge/common/hunt_nvram.c (revision 4557fabb34e865d7f40be64b39c9e34fa41dbb60)
1 /*-
2  * Copyright (c) 2012-2015 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 "efsys.h"
35 #include "efx.h"
36 #include "efx_types.h"
37 #include "efx_regs.h"
38 #include "efx_impl.h"
39 
40 #if EFSYS_OPT_HUNTINGTON
41 
42 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
43 
44 #include "ef10_tlv_layout.h"
45 
46 /* Cursor for TLV partition format */
47 typedef struct tlv_cursor_s {
48 	uint32_t	*block;			/* Base of data block */
49 	uint32_t	*current;		/* Cursor position */
50 	uint32_t	*end;			/* End tag position */
51 	uint32_t	*limit;			/* Last dword of data block */
52 } tlv_cursor_t;
53 
54 static	__checkReturn		efx_rc_t
55 tlv_validate_state(
56 	__in			tlv_cursor_t *cursor);
57 
58 
59 /*
60  * Operations on TLV formatted partition data.
61  */
62 static				uint32_t
tlv_tag(__in tlv_cursor_t * cursor)63 tlv_tag(
64 	__in	tlv_cursor_t	*cursor)
65 {
66 	uint32_t dword, tag;
67 
68 	dword = cursor->current[0];
69 	tag = __LE_TO_CPU_32(dword);
70 
71 	return (tag);
72 }
73 
74 static				size_t
tlv_length(__in tlv_cursor_t * cursor)75 tlv_length(
76 	__in	tlv_cursor_t	*cursor)
77 {
78 	uint32_t dword, length;
79 
80 	if (tlv_tag(cursor) == TLV_TAG_END)
81 		return (0);
82 
83 	dword = cursor->current[1];
84 	length = __LE_TO_CPU_32(dword);
85 
86 	return ((size_t)length);
87 }
88 
89 static				uint8_t *
tlv_value(__in tlv_cursor_t * cursor)90 tlv_value(
91 	__in	tlv_cursor_t	*cursor)
92 {
93 	if (tlv_tag(cursor) == TLV_TAG_END)
94 		return (NULL);
95 
96 	return ((uint8_t *)(&cursor->current[2]));
97 }
98 
99 static				uint8_t *
tlv_item(__in tlv_cursor_t * cursor)100 tlv_item(
101 	__in	tlv_cursor_t	*cursor)
102 {
103 	if (tlv_tag(cursor) == TLV_TAG_END)
104 		return (NULL);
105 
106 	return ((uint8_t *)cursor->current);
107 }
108 
109 /*
110  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
111  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
112  */
113 #define	TLV_DWORD_COUNT(length) \
114 	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
115 
116 
117 static				uint32_t *
tlv_next_item_ptr(__in tlv_cursor_t * cursor)118 tlv_next_item_ptr(
119 	__in	tlv_cursor_t	*cursor)
120 {
121 	uint32_t length;
122 
123 	length = tlv_length(cursor);
124 
125 	return (cursor->current + TLV_DWORD_COUNT(length));
126 }
127 
128 static				efx_rc_t
tlv_advance(__in tlv_cursor_t * cursor)129 tlv_advance(
130 	__in	tlv_cursor_t	*cursor)
131 {
132 	efx_rc_t rc;
133 
134 	if ((rc = tlv_validate_state(cursor)) != 0)
135 		goto fail1;
136 
137 	if (cursor->current == cursor->end) {
138 		/* No more tags after END tag */
139 		cursor->current = NULL;
140 		rc = ENOENT;
141 		goto fail2;
142 	}
143 
144 	/* Advance to next item and validate */
145 	cursor->current = tlv_next_item_ptr(cursor);
146 
147 	if ((rc = tlv_validate_state(cursor)) != 0)
148 		goto fail3;
149 
150 	return (0);
151 
152 fail3:
153 	EFSYS_PROBE(fail3);
154 fail2:
155 	EFSYS_PROBE(fail2);
156 fail1:
157 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
158 
159 	return (rc);
160 }
161 
162 static				efx_rc_t
tlv_rewind(__in tlv_cursor_t * cursor)163 tlv_rewind(
164 	__in	tlv_cursor_t	*cursor)
165 {
166 	efx_rc_t rc;
167 
168 	cursor->current = cursor->block;
169 
170 	if ((rc = tlv_validate_state(cursor)) != 0)
171 		goto fail1;
172 
173 	return (0);
174 
175 fail1:
176 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
177 
178 	return (rc);
179 }
180 
181 static				efx_rc_t
tlv_find(__in tlv_cursor_t * cursor,__in uint32_t tag)182 tlv_find(
183 	__in	tlv_cursor_t	*cursor,
184 	__in	uint32_t	tag)
185 {
186 	efx_rc_t rc;
187 
188 	rc = tlv_rewind(cursor);
189 	while (rc == 0) {
190 		if (tlv_tag(cursor) == tag)
191 			break;
192 
193 		rc = tlv_advance(cursor);
194 	}
195 	return (rc);
196 }
197 
198 static	__checkReturn		efx_rc_t
tlv_validate_state(__in tlv_cursor_t * cursor)199 tlv_validate_state(
200 	__in	tlv_cursor_t	*cursor)
201 {
202 	efx_rc_t rc;
203 
204 	/* Check cursor position */
205 	if (cursor->current < cursor->block) {
206 		rc = EINVAL;
207 		goto fail1;
208 	}
209 	if (cursor->current > cursor->limit) {
210 		rc = EINVAL;
211 		goto fail2;
212 	}
213 
214 	if (tlv_tag(cursor) != TLV_TAG_END) {
215 		/* Check current item has space for tag and length */
216 		if (cursor->current > (cursor->limit - 2)) {
217 			cursor->current = NULL;
218 			rc = EFAULT;
219 			goto fail3;
220 		}
221 
222 		/* Check we have value data for current item and another tag */
223 		if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
224 			cursor->current = NULL;
225 			rc = EFAULT;
226 			goto fail4;
227 		}
228 	}
229 
230 	return (0);
231 
232 fail4:
233 	EFSYS_PROBE(fail4);
234 fail3:
235 	EFSYS_PROBE(fail3);
236 fail2:
237 	EFSYS_PROBE(fail2);
238 fail1:
239 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
240 
241 	return (rc);
242 }
243 
244 static				efx_rc_t
tlv_init_cursor(__in tlv_cursor_t * cursor,__in uint32_t * block,__in uint32_t * limit)245 tlv_init_cursor(
246 	__in	tlv_cursor_t	*cursor,
247 	__in	uint32_t	*block,
248 	__in	uint32_t	*limit)
249 {
250 	cursor->block	= block;
251 	cursor->limit	= limit;
252 
253 	cursor->current	= cursor->block;
254 	cursor->end	= NULL;
255 
256 	return (tlv_validate_state(cursor));
257 }
258 
259 static				efx_rc_t
tlv_init_cursor_from_size(__in tlv_cursor_t * cursor,__in uint8_t * block,__in size_t size)260 tlv_init_cursor_from_size(
261 	__in	tlv_cursor_t	*cursor,
262 	__in	uint8_t	*block,
263 	__in	size_t		size)
264 {
265 	uint32_t *limit;
266 	limit = (uint32_t *)(block + size - sizeof (uint32_t));
267 	return (tlv_init_cursor(cursor, (uint32_t *)block, limit));
268 }
269 
270 static				efx_rc_t
tlv_require_end(__in tlv_cursor_t * cursor)271 tlv_require_end(
272 	__in	tlv_cursor_t	*cursor)
273 {
274 	uint32_t *pos;
275 	efx_rc_t rc;
276 
277 	if (cursor->end == NULL) {
278 		pos = cursor->current;
279 		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
280 			goto fail1;
281 
282 		cursor->end = cursor->current;
283 		cursor->current = pos;
284 	}
285 
286 	return (0);
287 
288 fail1:
289 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
290 
291 	return (rc);
292 }
293 
294 static				size_t
tlv_block_length_used(__in tlv_cursor_t * cursor)295 tlv_block_length_used(
296 	__in	tlv_cursor_t	*cursor)
297 {
298 	efx_rc_t rc;
299 
300 	if ((rc = tlv_validate_state(cursor)) != 0)
301 		goto fail1;
302 
303 	if ((rc = tlv_require_end(cursor)) != 0)
304 		goto fail2;
305 
306 	/* Return space used (including the END tag) */
307 	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
308 
309 fail2:
310 	EFSYS_PROBE(fail2);
311 fail1:
312 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
313 
314 	return (0);
315 }
316 
317 
318 static	__checkReturn		uint32_t *
tlv_write(__in tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)319 tlv_write(
320 	__in			tlv_cursor_t *cursor,
321 	__in			uint32_t tag,
322 	__in_bcount(size)	uint8_t *data,
323 	__in			size_t size)
324 {
325 	uint32_t len = size;
326 	uint32_t *ptr;
327 
328 	ptr = cursor->current;
329 
330 	*ptr++ = __CPU_TO_LE_32(tag);
331 	*ptr++ = __CPU_TO_LE_32(len);
332 
333 	if (len > 0) {
334 		ptr[(len - 1) / sizeof (uint32_t)] = 0;
335 		memcpy(ptr, data, len);
336 		ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
337 	}
338 
339 	return (ptr);
340 }
341 
342 static	__checkReturn		efx_rc_t
tlv_insert(__in tlv_cursor_t * cursor,__in uint32_t tag,__in uint8_t * data,__in size_t size)343 tlv_insert(
344 	__in	tlv_cursor_t	*cursor,
345 	__in	uint32_t	tag,
346 	__in	uint8_t		*data,
347 	__in	size_t		size)
348 {
349 	unsigned int delta;
350 	efx_rc_t rc;
351 
352 	if ((rc = tlv_validate_state(cursor)) != 0)
353 		goto fail1;
354 
355 	if ((rc = tlv_require_end(cursor)) != 0)
356 		goto fail2;
357 
358 	if (tag == TLV_TAG_END) {
359 		rc = EINVAL;
360 		goto fail3;
361 	}
362 
363 	delta = TLV_DWORD_COUNT(size);
364 	if (cursor->end + 1 + delta > cursor->limit) {
365 		rc = ENOSPC;
366 		goto fail4;
367 	}
368 
369 	/* Move data up: new space at cursor->current */
370 	memmove(cursor->current + delta, cursor->current,
371 	    (cursor->end + 1 - cursor->current) * sizeof (uint32_t));
372 
373 	/* Adjust the end pointer */
374 	cursor->end += delta;
375 
376 	/* Write new TLV item */
377 	tlv_write(cursor, tag, data, size);
378 
379 	return (0);
380 
381 fail4:
382 	EFSYS_PROBE(fail4);
383 fail3:
384 	EFSYS_PROBE(fail3);
385 fail2:
386 	EFSYS_PROBE(fail2);
387 fail1:
388 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
389 
390 	return (rc);
391 }
392 
393 static	__checkReturn		efx_rc_t
tlv_modify(__in tlv_cursor_t * cursor,__in uint32_t tag,__in uint8_t * data,__in size_t size)394 tlv_modify(
395 	__in	tlv_cursor_t	*cursor,
396 	__in	uint32_t	tag,
397 	__in	uint8_t		*data,
398 	__in	size_t		size)
399 {
400 	uint32_t *pos;
401 	unsigned int old_ndwords;
402 	unsigned int new_ndwords;
403 	unsigned int delta;
404 	efx_rc_t rc;
405 
406 	if ((rc = tlv_validate_state(cursor)) != 0)
407 		goto fail1;
408 
409 	if (tlv_tag(cursor) == TLV_TAG_END) {
410 		rc = EINVAL;
411 		goto fail2;
412 	}
413 	if (tlv_tag(cursor) != tag) {
414 		rc = EINVAL;
415 		goto fail3;
416 	}
417 
418 	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
419 	new_ndwords = TLV_DWORD_COUNT(size);
420 
421 	if ((rc = tlv_require_end(cursor)) != 0)
422 		goto fail4;
423 
424 	if (new_ndwords > old_ndwords) {
425 		/* Expand space used for TLV item */
426 		delta = new_ndwords - old_ndwords;
427 		pos = cursor->current + old_ndwords;
428 
429 		if (cursor->end + 1 + delta > cursor->limit) {
430 			rc = ENOSPC;
431 			goto fail5;
432 		}
433 
434 		/* Move up: new space at (cursor->current + old_ndwords) */
435 		memmove(pos + delta, pos,
436 		    (cursor->end + 1 - pos) * sizeof (uint32_t));
437 
438 		/* Adjust the end pointer */
439 		cursor->end += delta;
440 
441 	} else if (new_ndwords < old_ndwords) {
442 		/* Shrink space used for TLV item */
443 		delta = old_ndwords - new_ndwords;
444 		pos = cursor->current + new_ndwords;
445 
446 		/* Move down: remove words at (cursor->current + new_ndwords) */
447 		memmove(pos, pos + delta,
448 		    (cursor->end + 1 - pos) * sizeof (uint32_t));
449 
450 		/* Zero the new space at the end of the TLV chain */
451 		memset(cursor->end + 1 - delta, 0, delta * sizeof (uint32_t));
452 
453 		/* Adjust the end pointer */
454 		cursor->end -= delta;
455 	}
456 
457 	/* Write new data */
458 	tlv_write(cursor, tag, data, size);
459 
460 	return (0);
461 
462 fail5:
463 	EFSYS_PROBE(fail5);
464 fail4:
465 	EFSYS_PROBE(fail4);
466 fail3:
467 	EFSYS_PROBE(fail3);
468 fail2:
469 	EFSYS_PROBE(fail2);
470 fail1:
471 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
472 
473 	return (rc);
474 }
475 
476 /* Validate TLV formatted partition contents (before writing to flash) */
477 	__checkReturn		efx_rc_t
efx_nvram_tlv_validate(__in efx_nic_t * enp,__in uint32_t partn,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)478 efx_nvram_tlv_validate(
479 	__in			efx_nic_t *enp,
480 	__in			uint32_t partn,
481 	__in_bcount(partn_size)	caddr_t partn_data,
482 	__in			size_t partn_size)
483 {
484 	tlv_cursor_t cursor;
485 	struct tlv_partition_header *header;
486 	struct tlv_partition_trailer *trailer;
487 	size_t total_length;
488 	uint32_t cksum;
489 	int pos;
490 	efx_rc_t rc;
491 
492 	EFX_STATIC_ASSERT(sizeof (*header) <= HUNTINGTON_NVRAM_CHUNK);
493 
494 	if ((partn_data == NULL) || (partn_size == 0)) {
495 		rc = EINVAL;
496 		goto fail1;
497 	}
498 
499 	/* The partition header must be the first item (at offset zero) */
500 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
501 		    partn_size)) != 0) {
502 		rc = EFAULT;
503 		goto fail2;
504 	}
505 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
506 		rc = EINVAL;
507 		goto fail3;
508 	}
509 	header = (struct tlv_partition_header *)tlv_item(&cursor);
510 
511 	/* Check TLV partition length (includes the END tag) */
512 	total_length = __LE_TO_CPU_32(header->total_length);
513 	if (total_length > partn_size) {
514 		rc = EFBIG;
515 		goto fail4;
516 	}
517 
518 	/* Check partition ends with PARTITION_TRAILER and END tags */
519 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
520 		rc = EINVAL;
521 		goto fail5;
522 	}
523 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
524 
525 	if ((rc = tlv_advance(&cursor)) != 0) {
526 		rc = EINVAL;
527 		goto fail6;
528 	}
529 	if (tlv_tag(&cursor) != TLV_TAG_END) {
530 		rc = EINVAL;
531 		goto fail7;
532 	}
533 
534 	/* Check generation counts are consistent */
535 	if (trailer->generation != header->generation) {
536 		rc = EINVAL;
537 		goto fail8;
538 	}
539 
540 	/* Verify partition checksum */
541 	cksum = 0;
542 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
543 		cksum += *((uint32_t *)(partn_data + pos));
544 	}
545 	if (cksum != 0) {
546 		rc = EINVAL;
547 		goto fail9;
548 	}
549 
550 	return (0);
551 
552 fail9:
553 	EFSYS_PROBE(fail9);
554 fail8:
555 	EFSYS_PROBE(fail8);
556 fail7:
557 	EFSYS_PROBE(fail7);
558 fail6:
559 	EFSYS_PROBE(fail6);
560 fail5:
561 	EFSYS_PROBE(fail5);
562 fail4:
563 	EFSYS_PROBE(fail4);
564 fail3:
565 	EFSYS_PROBE(fail3);
566 fail2:
567 	EFSYS_PROBE(fail2);
568 fail1:
569 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
570 
571 	return (rc);
572 }
573 
574 /*
575  * Read and validate a segment from a partition. A segment is a complete
576  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
577  * be multiple segments in a partition, so seg_offset allows segments
578  * beyond the first to be read.
579  */
580 static	__checkReturn			efx_rc_t
hunt_nvram_read_tlv_segment(__in efx_nic_t * enp,__in uint32_t partn,__in size_t seg_offset,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size)581 hunt_nvram_read_tlv_segment(
582 	__in				efx_nic_t *enp,
583 	__in				uint32_t partn,
584 	__in				size_t seg_offset,
585 	__in_bcount(max_seg_size)	caddr_t seg_data,
586 	__in				size_t max_seg_size)
587 {
588 	tlv_cursor_t cursor;
589 	struct tlv_partition_header *header;
590 	struct tlv_partition_trailer *trailer;
591 	size_t total_length;
592 	uint32_t cksum;
593 	int pos;
594 	efx_rc_t rc;
595 
596 	EFX_STATIC_ASSERT(sizeof (*header) <= HUNTINGTON_NVRAM_CHUNK);
597 
598 	if ((seg_data == NULL) || (max_seg_size == 0)) {
599 		rc = EINVAL;
600 		goto fail1;
601 	}
602 
603 	/* Read initial chunk of the segment, starting at offset */
604 	if ((rc = hunt_nvram_partn_read(enp, partn, seg_offset, seg_data,
605 		    HUNTINGTON_NVRAM_CHUNK)) != 0) {
606 		goto fail2;
607 	}
608 
609 	/* A PARTITION_HEADER tag must be the first item at the given offset */
610 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
611 		    max_seg_size)) != 0) {
612 		rc = EFAULT;
613 		goto fail3;
614 	}
615 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
616 		rc = EINVAL;
617 		goto fail4;
618 	}
619 	header = (struct tlv_partition_header *)tlv_item(&cursor);
620 
621 	/* Check TLV segment length (includes the END tag) */
622 	total_length = __LE_TO_CPU_32(header->total_length);
623 	if (total_length > max_seg_size) {
624 		rc = EFBIG;
625 		goto fail5;
626 	}
627 
628 	/* Read the remaining segment content */
629 	if (total_length > HUNTINGTON_NVRAM_CHUNK) {
630 		if ((rc = hunt_nvram_partn_read(enp, partn,
631 			    seg_offset + HUNTINGTON_NVRAM_CHUNK,
632 			    seg_data + HUNTINGTON_NVRAM_CHUNK,
633 			    total_length - HUNTINGTON_NVRAM_CHUNK)) != 0)
634 			goto fail6;
635 	}
636 
637 	/* Check segment ends with PARTITION_TRAILER and END tags */
638 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
639 		rc = EINVAL;
640 		goto fail7;
641 	}
642 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
643 
644 	if ((rc = tlv_advance(&cursor)) != 0) {
645 		rc = EINVAL;
646 		goto fail8;
647 	}
648 	if (tlv_tag(&cursor) != TLV_TAG_END) {
649 		rc = EINVAL;
650 		goto fail9;
651 	}
652 
653 	/* Check data read from segment is consistent */
654 	if (trailer->generation != header->generation) {
655 		/*
656 		 * The partition data may have been modified between successive
657 		 * MCDI NVRAM_READ requests by the MC or another PCI function.
658 		 *
659 		 * The caller must retry to obtain consistent partition data.
660 		 */
661 		rc = EAGAIN;
662 		goto fail10;
663 	}
664 
665 	/* Verify segment checksum */
666 	cksum = 0;
667 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
668 		cksum += *((uint32_t *)(seg_data + pos));
669 	}
670 	if (cksum != 0) {
671 		rc = EINVAL;
672 		goto fail11;
673 	}
674 
675 	return (0);
676 
677 fail11:
678 	EFSYS_PROBE(fail11);
679 fail10:
680 	EFSYS_PROBE(fail10);
681 fail9:
682 	EFSYS_PROBE(fail9);
683 fail8:
684 	EFSYS_PROBE(fail8);
685 fail7:
686 	EFSYS_PROBE(fail7);
687 fail6:
688 	EFSYS_PROBE(fail6);
689 fail5:
690 	EFSYS_PROBE(fail5);
691 fail4:
692 	EFSYS_PROBE(fail4);
693 fail3:
694 	EFSYS_PROBE(fail3);
695 fail2:
696 	EFSYS_PROBE(fail2);
697 fail1:
698 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
699 
700 	return (rc);
701 }
702 
703 /*
704  * Read a single TLV item from a host memory
705  * buffer containing a TLV formatted segment.
706  */
707 	__checkReturn		efx_rc_t
hunt_nvram_buf_read_tlv(__in efx_nic_t * enp,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__deref_out_bcount_opt (* sizep)caddr_t * datap,__out size_t * sizep)708 hunt_nvram_buf_read_tlv(
709 	__in				efx_nic_t *enp,
710 	__in_bcount(max_seg_size)	caddr_t seg_data,
711 	__in				size_t max_seg_size,
712 	__in				uint32_t tag,
713 	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
714 	__out				size_t *sizep)
715 {
716 	tlv_cursor_t cursor;
717 	caddr_t data;
718 	size_t length;
719 	caddr_t value;
720 	efx_rc_t rc;
721 
722 	if ((seg_data == NULL) || (max_seg_size == 0)) {
723 		rc = EINVAL;
724 		goto fail1;
725 	}
726 
727 	/* Find requested TLV tag in segment data */
728 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
729 		    max_seg_size)) != 0) {
730 		rc = EFAULT;
731 		goto fail2;
732 	}
733 	if ((rc = tlv_find(&cursor, tag)) != 0) {
734 		rc = ENOENT;
735 		goto fail3;
736 	}
737 	value = (caddr_t)tlv_value(&cursor);
738 	length = tlv_length(&cursor);
739 
740 	if (length == 0)
741 		data = NULL;
742 	else {
743 		/* Copy out data from TLV item */
744 		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
745 		if (data == NULL) {
746 			rc = ENOMEM;
747 			goto fail4;
748 		}
749 		memcpy(data, value, length);
750 	}
751 
752 	*datap = data;
753 	*sizep = length;
754 
755 	return (0);
756 
757 fail4:
758 	EFSYS_PROBE(fail4);
759 fail3:
760 	EFSYS_PROBE(fail3);
761 fail2:
762 	EFSYS_PROBE(fail2);
763 fail1:
764 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
765 
766 	return (rc);
767 }
768 
769 /* Read a single TLV item from the first segment in a TLV formatted partition */
770 	__checkReturn		efx_rc_t
hunt_nvram_partn_read_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__deref_out_bcount_opt (* seg_sizep)caddr_t * seg_datap,__out size_t * seg_sizep)771 hunt_nvram_partn_read_tlv(
772 	__in					efx_nic_t *enp,
773 	__in					uint32_t partn,
774 	__in					uint32_t tag,
775 	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
776 	__out					size_t *seg_sizep)
777 {
778 	caddr_t seg_data = NULL;
779 	size_t partn_size = 0;
780 	size_t length;
781 	caddr_t data;
782 	int retry;
783 	efx_rc_t rc;
784 
785 	/* Allocate sufficient memory for the entire partition */
786 	if ((rc = hunt_nvram_partn_size(enp, partn, &partn_size)) != 0)
787 		goto fail1;
788 
789 	if (partn_size == 0) {
790 		rc = ENOENT;
791 		goto fail2;
792 	}
793 
794 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
795 	if (seg_data == NULL) {
796 		rc = ENOMEM;
797 		goto fail3;
798 	}
799 
800 	/*
801 	 * Read the first segment in a TLV partition. Retry until consistent
802 	 * segment contents are returned. Inconsistent data may be read if:
803 	 *  a) the segment contents are invalid
804 	 *  b) the MC has rebooted while we were reading the partition
805 	 *  c) the partition has been modified while we were reading it
806 	 * Limit retry attempts to ensure forward progress.
807 	 */
808 	retry = 10;
809 	do {
810 		rc = hunt_nvram_read_tlv_segment(enp, partn, 0,
811 		    seg_data, partn_size);
812 	} while ((rc == EAGAIN) && (--retry > 0));
813 
814 	if (rc != 0) {
815 		/* Failed to obtain consistent segment data */
816 		goto fail4;
817 	}
818 
819 	if ((rc = hunt_nvram_buf_read_tlv(enp, seg_data, partn_size,
820 		    tag, &data, &length)) != 0)
821 		goto fail5;
822 
823 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
824 
825 	*seg_datap = data;
826 	*seg_sizep = length;
827 
828 	return (0);
829 
830 fail5:
831 	EFSYS_PROBE(fail5);
832 fail4:
833 	EFSYS_PROBE(fail4);
834 
835 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
836 fail3:
837 	EFSYS_PROBE(fail3);
838 fail2:
839 	EFSYS_PROBE(fail2);
840 fail1:
841 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
842 
843 	return (rc);
844 }
845 
846 /* Compute the size of a segment. */
847 	static	__checkReturn	efx_rc_t
hunt_nvram_buf_segment_size(__in caddr_t seg_data,__in size_t max_seg_size,__out size_t * seg_sizep)848 hunt_nvram_buf_segment_size(
849 	__in			caddr_t seg_data,
850 	__in			size_t max_seg_size,
851 	__out			size_t *seg_sizep)
852 {
853 	efx_rc_t rc;
854 	tlv_cursor_t cursor;
855 	struct tlv_partition_header *header;
856 	uint32_t cksum;
857 	int pos;
858 	uint32_t *end_tag_position;
859 	uint32_t segment_length;
860 
861 	/* A PARTITION_HEADER tag must be the first item at the given offset */
862 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
863 		    max_seg_size)) != 0) {
864 		rc = EFAULT;
865 		goto fail1;
866 	}
867 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
868 		rc = EINVAL;
869 		goto fail2;
870 	}
871 	header = (struct tlv_partition_header *)tlv_item(&cursor);
872 
873 	/* Check TLV segment length (includes the END tag) */
874 	*seg_sizep = __LE_TO_CPU_32(header->total_length);
875 	if (*seg_sizep > max_seg_size) {
876 		rc = EFBIG;
877 		goto fail3;
878 	}
879 
880 	/* Check segment ends with PARTITION_TRAILER and END tags */
881 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
882 		rc = EINVAL;
883 		goto fail4;
884 	}
885 
886 	if ((rc = tlv_advance(&cursor)) != 0) {
887 		rc = EINVAL;
888 		goto fail5;
889 	}
890 	if (tlv_tag(&cursor) != TLV_TAG_END) {
891 		rc = EINVAL;
892 		goto fail6;
893 	}
894 	end_tag_position = cursor.current;
895 
896 	/* Verify segment checksum */
897 	cksum = 0;
898 	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
899 		cksum += *((uint32_t *)(seg_data + pos));
900 	}
901 	if (cksum != 0) {
902 		rc = EINVAL;
903 		goto fail7;
904 	}
905 
906 	/*
907 	 * Calculate total length from HEADER to END tags and compare to
908 	 * max_seg_size and the total_length field in the HEADER tag.
909 	 */
910 	segment_length = tlv_block_length_used(&cursor);
911 
912 	if (segment_length > max_seg_size) {
913 		rc = EINVAL;
914 		goto fail8;
915 	}
916 
917 	if (segment_length != *seg_sizep) {
918 		rc = EINVAL;
919 		goto fail9;
920 	}
921 
922 	/* Skip over the first HEADER tag. */
923 	rc = tlv_rewind(&cursor);
924 	rc = tlv_advance(&cursor);
925 
926 	while (rc == 0) {
927 		if (tlv_tag(&cursor) == TLV_TAG_END) {
928 			/* Check that the END tag is the one found earlier. */
929 			if (cursor.current != end_tag_position)
930 				goto fail10;
931 			break;
932 		}
933 		/* Check for duplicate HEADER tags before the END tag. */
934 		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
935 			rc = EINVAL;
936 			goto fail11;
937 		}
938 
939 		rc = tlv_advance(&cursor);
940 	}
941 	if (rc != 0)
942 		goto fail12;
943 
944 	return (0);
945 
946 fail12:
947 	EFSYS_PROBE(fail12);
948 fail11:
949 	EFSYS_PROBE(fail11);
950 fail10:
951 	EFSYS_PROBE(fail10);
952 fail9:
953 	EFSYS_PROBE(fail9);
954 fail8:
955 	EFSYS_PROBE(fail8);
956 fail7:
957 	EFSYS_PROBE(fail7);
958 fail6:
959 	EFSYS_PROBE(fail6);
960 fail5:
961 	EFSYS_PROBE(fail5);
962 fail4:
963 	EFSYS_PROBE(fail4);
964 fail3:
965 	EFSYS_PROBE(fail3);
966 fail2:
967 	EFSYS_PROBE(fail2);
968 fail1:
969 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
970 
971 	return (rc);
972 }
973 
974 /*
975  * Add or update a single TLV item in a host memory buffer containing a TLV
976  * formatted segment. Historically partitions consisted of only one segment.
977  */
978 	__checkReturn			efx_rc_t
hunt_nvram_buf_write_tlv(__inout_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__in_bcount (tag_size)caddr_t tag_data,__in size_t tag_size,__out size_t * total_lengthp)979 hunt_nvram_buf_write_tlv(
980 	__inout_bcount(max_seg_size)	caddr_t seg_data,
981 	__in				size_t max_seg_size,
982 	__in				uint32_t tag,
983 	__in_bcount(tag_size)		caddr_t tag_data,
984 	__in				size_t tag_size,
985 	__out				size_t *total_lengthp)
986 {
987 	tlv_cursor_t cursor;
988 	struct tlv_partition_header *header;
989 	struct tlv_partition_trailer *trailer;
990 	uint32_t generation;
991 	uint32_t cksum;
992 	int pos;
993 	efx_rc_t rc;
994 
995 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
996 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
997 			max_seg_size)) != 0) {
998 		rc = EFAULT;
999 		goto fail1;
1000 	}
1001 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1002 		rc = EINVAL;
1003 		goto fail2;
1004 	}
1005 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1006 
1007 	/* Update the TLV chain to contain the new data */
1008 	if ((rc = tlv_find(&cursor, tag)) == 0) {
1009 		/* Modify existing TLV item */
1010 		if ((rc = tlv_modify(&cursor, tag,
1011 			    (uint8_t *)tag_data, tag_size)) != 0)
1012 			goto fail3;
1013 	} else {
1014 		/* Insert a new TLV item before the PARTITION_TRAILER */
1015 		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1016 		if (rc != 0) {
1017 			rc = EINVAL;
1018 			goto fail4;
1019 		}
1020 		if ((rc = tlv_insert(&cursor, tag,
1021 			    (uint8_t *)tag_data, tag_size)) != 0) {
1022 			rc = EINVAL;
1023 			goto fail5;
1024 		}
1025 	}
1026 
1027 	/* Find the trailer tag */
1028 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1029 		rc = EINVAL;
1030 		goto fail6;
1031 	}
1032 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1033 
1034 	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1035 	*total_lengthp = tlv_block_length_used(&cursor);
1036 	if (*total_lengthp > max_seg_size) {
1037 		rc = ENOSPC;
1038 		goto fail7;
1039 	}
1040 	generation = __LE_TO_CPU_32(header->generation) + 1;
1041 
1042 	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1043 	header->generation	= __CPU_TO_LE_32(generation);
1044 	trailer->generation	= __CPU_TO_LE_32(generation);
1045 
1046 	/* Recompute PARTITION_TRAILER checksum */
1047 	trailer->checksum = 0;
1048 	cksum = 0;
1049 	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1050 		cksum += *((uint32_t *)(seg_data + pos));
1051 	}
1052 	trailer->checksum = ~cksum + 1;
1053 
1054 	return (0);
1055 
1056 fail7:
1057 	EFSYS_PROBE(fail7);
1058 fail6:
1059 	EFSYS_PROBE(fail6);
1060 fail5:
1061 	EFSYS_PROBE(fail5);
1062 fail4:
1063 	EFSYS_PROBE(fail4);
1064 fail3:
1065 	EFSYS_PROBE(fail3);
1066 fail2:
1067 	EFSYS_PROBE(fail2);
1068 fail1:
1069 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1070 
1071 	return (rc);
1072 }
1073 
1074 /*
1075  * Add or update a single TLV item in the first segment of a TLV formatted
1076  * dynamic config partition. The first segment is the current active
1077  * configuration.
1078  */
1079 	__checkReturn		efx_rc_t
hunt_nvram_partn_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size)1080 hunt_nvram_partn_write_tlv(
1081 	__in			efx_nic_t *enp,
1082 	__in			uint32_t partn,
1083 	__in			uint32_t tag,
1084 	__in_bcount(size)	caddr_t data,
1085 	__in			size_t size)
1086 {
1087 	return hunt_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1088 	    size, B_FALSE);
1089 }
1090 
1091 /*
1092  * Read a segment from nvram at the given offset into a buffer (segment_data)
1093  * and optionally write a new tag to it.
1094  */
1095 	static	__checkReturn	efx_rc_t
hunt_nvram_segment_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__inout caddr_t * seg_datap,__inout size_t * partn_offsetp,__inout size_t * src_remain_lenp,__inout size_t * dest_remain_lenp,__in boolean_t write)1096 hunt_nvram_segment_write_tlv(
1097 	__in			efx_nic_t *enp,
1098 	__in			uint32_t partn,
1099 	__in			uint32_t tag,
1100 	__in_bcount(size)	caddr_t data,
1101 	__in			size_t size,
1102 	__inout			caddr_t *seg_datap,
1103 	__inout			size_t *partn_offsetp,
1104 	__inout			size_t *src_remain_lenp,
1105 	__inout			size_t *dest_remain_lenp,
1106 	__in			boolean_t write)
1107 {
1108 	efx_rc_t rc;
1109 	int status;
1110 	size_t original_segment_size;
1111 	size_t modified_segment_size;
1112 
1113 	/*
1114 	 * Read the segment from NVRAM into the segment_data buffer and validate
1115 	 * it, returning if it does not validate. This is not a failure unless
1116 	 * this is the first segment in a partition. In this case the caller
1117 	 * must propogate the error.
1118 	 */
1119 	status = hunt_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1120 	    *seg_datap, *src_remain_lenp);
1121 	if (status != 0)
1122 		return (EINVAL);
1123 
1124 	status = hunt_nvram_buf_segment_size(*seg_datap,
1125 	    *src_remain_lenp, &original_segment_size);
1126 	if (status != 0)
1127 		return (EINVAL);
1128 
1129 	if (write) {
1130 		/* Update the contents of the segment in the buffer */
1131 		if ((rc = hunt_nvram_buf_write_tlv(*seg_datap,
1132 			*dest_remain_lenp, tag, data, size,
1133 			&modified_segment_size)) != 0)
1134 			goto fail1;
1135 		*dest_remain_lenp -= modified_segment_size;
1136 		*seg_datap += modified_segment_size;
1137 	} else {
1138 		/*
1139 		 * We won't modify this segment, but still need to update the
1140 		 * remaining lengths and pointers.
1141 		 */
1142 		*dest_remain_lenp -= original_segment_size;
1143 		*seg_datap += original_segment_size;
1144 	}
1145 
1146 	*partn_offsetp += original_segment_size;
1147 	*src_remain_lenp -= original_segment_size;
1148 
1149 	return (0);
1150 
1151 fail1:
1152 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1153 
1154 	return (rc);
1155 }
1156 
1157 /*
1158  * Add or update a single TLV item in either the first segment or in all
1159  * segments in a TLV formatted dynamic config partition. Dynamic config
1160  * partitions on boards that support RFID are divided into a number of segments,
1161  * each formatted like a partition, with header, trailer and end tags. The first
1162  * segment is the current active configuration.
1163  *
1164  * The segments are initialised by manftest and each contain a different
1165  * configuration e.g. firmware variant. The firmware can be instructed
1166  * via RFID to copy a segment to replace the first segment, hence changing the
1167  * active configuration.  This allows ops to change the configuration of a board
1168  * prior to shipment using RFID.
1169  *
1170  * Changes to the dynamic config may need to be written to all segments (e.g.
1171  * firmware versions) or just the first segment (changes to the active
1172  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1173  * If only the first segment is written the code still needs to be aware of the
1174  * possible presence of subsequent segments as writing to a segment may cause
1175  * its size to increase, which would overwrite the subsequent segments and
1176  * invalidate them.
1177  */
1178 	__checkReturn		efx_rc_t
hunt_nvram_partn_write_segment_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__in boolean_t all_segments)1179 hunt_nvram_partn_write_segment_tlv(
1180 	__in			efx_nic_t *enp,
1181 	__in			uint32_t partn,
1182 	__in			uint32_t tag,
1183 	__in_bcount(size)	caddr_t data,
1184 	__in			size_t size,
1185 	__in			boolean_t all_segments)
1186 {
1187 	size_t partn_size = 0;
1188 	caddr_t partn_data;
1189 	size_t total_length = 0;
1190 	efx_rc_t rc;
1191 	size_t current_offset = 0;
1192 	size_t remaining_original_length;
1193 	size_t remaining_modified_length;
1194 	caddr_t segment_data;
1195 
1196 	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1197 
1198 	/* Allocate sufficient memory for the entire partition */
1199 	if ((rc = hunt_nvram_partn_size(enp, partn, &partn_size)) != 0)
1200 		goto fail1;
1201 
1202 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1203 	if (partn_data == NULL) {
1204 		rc = ENOMEM;
1205 		goto fail2;
1206 	}
1207 
1208 	remaining_original_length = partn_size;
1209 	remaining_modified_length = partn_size;
1210 	segment_data = partn_data;
1211 
1212 	/* Lock the partition */
1213 	if ((rc = hunt_nvram_partn_lock(enp, partn)) != 0)
1214 		goto fail3;
1215 
1216 	/* Iterate over each (potential) segment to update it. */
1217 	do {
1218 		boolean_t write = all_segments || current_offset == 0;
1219 
1220 		rc = hunt_nvram_segment_write_tlv(enp, partn, tag, data, size,
1221 		    &segment_data, &current_offset, &remaining_original_length,
1222 		    &remaining_modified_length, write);
1223 		if (rc != 0) {
1224 			if (current_offset == 0) {
1225 				/*
1226 				 * If no data has been read then the first
1227 				 * segment is invalid, which is an error.
1228 				 */
1229 				goto fail4;
1230 			}
1231 			break;
1232 		}
1233 	} while (current_offset < partn_size);
1234 
1235 	total_length = segment_data - partn_data;
1236 
1237 	/*
1238 	 * We've run out of space.  This should actually be dealt with by
1239 	 * hunt_nvram_buf_write_tlv returning ENOSPC.
1240 	 */
1241 	if (total_length > partn_size) {
1242 		rc = ENOSPC;
1243 		goto fail5;
1244 	}
1245 
1246 	/* Erase the whole partition in NVRAM */
1247 	if ((rc = hunt_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1248 		goto fail6;
1249 
1250 	/* Write new partition contents from the buffer to NVRAM */
1251 	if ((rc = hunt_nvram_partn_write(enp, partn, 0, partn_data,
1252 		    total_length)) != 0)
1253 		goto fail7;
1254 
1255 	/* Unlock the partition */
1256 	hunt_nvram_partn_unlock(enp, partn);
1257 
1258 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1259 
1260 	return (0);
1261 
1262 fail7:
1263 	EFSYS_PROBE(fail7);
1264 fail6:
1265 	EFSYS_PROBE(fail6);
1266 fail5:
1267 	EFSYS_PROBE(fail5);
1268 fail4:
1269 	EFSYS_PROBE(fail4);
1270 
1271 	hunt_nvram_partn_unlock(enp, partn);
1272 fail3:
1273 	EFSYS_PROBE(fail3);
1274 
1275 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1276 fail2:
1277 	EFSYS_PROBE(fail2);
1278 fail1:
1279 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1280 
1281 	return (rc);
1282 }
1283 
1284 /*
1285  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1286  * not the data used by the segments in the partition.
1287  */
1288 	__checkReturn		efx_rc_t
hunt_nvram_partn_size(__in efx_nic_t * enp,__in unsigned int partn,__out size_t * sizep)1289 hunt_nvram_partn_size(
1290 	__in			efx_nic_t *enp,
1291 	__in			unsigned int partn,
1292 	__out			size_t *sizep)
1293 {
1294 	efx_rc_t rc;
1295 
1296 	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1297 	    NULL, NULL, NULL)) != 0)
1298 		goto fail1;
1299 
1300 	return (0);
1301 
1302 fail1:
1303 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1304 
1305 	return (rc);
1306 }
1307 
1308 	__checkReturn		efx_rc_t
hunt_nvram_partn_lock(__in efx_nic_t * enp,__in unsigned int partn)1309 hunt_nvram_partn_lock(
1310 	__in			efx_nic_t *enp,
1311 	__in			unsigned int partn)
1312 {
1313 	efx_rc_t rc;
1314 
1315 	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1316 		goto fail1;
1317 
1318 	return (0);
1319 
1320 fail1:
1321 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1322 
1323 	return (rc);
1324 }
1325 
1326 	__checkReturn		efx_rc_t
hunt_nvram_partn_read(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1327 hunt_nvram_partn_read(
1328 	__in			efx_nic_t *enp,
1329 	__in			unsigned int partn,
1330 	__in			unsigned int offset,
1331 	__out_bcount(size)	caddr_t data,
1332 	__in			size_t size)
1333 {
1334 	size_t chunk;
1335 	efx_rc_t rc;
1336 
1337 	while (size > 0) {
1338 		chunk = MIN(size, HUNTINGTON_NVRAM_CHUNK);
1339 
1340 		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1341 			    data, chunk)) != 0) {
1342 			goto fail1;
1343 		}
1344 
1345 		size -= chunk;
1346 		data += chunk;
1347 		offset += chunk;
1348 	}
1349 
1350 	return (0);
1351 
1352 fail1:
1353 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1354 
1355 	return (rc);
1356 }
1357 
1358 	__checkReturn		efx_rc_t
hunt_nvram_partn_erase(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__in size_t size)1359 hunt_nvram_partn_erase(
1360 	__in			efx_nic_t *enp,
1361 	__in			unsigned int partn,
1362 	__in			unsigned int offset,
1363 	__in			size_t size)
1364 {
1365 	efx_rc_t rc;
1366 	uint32_t erase_size;
1367 
1368 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1369 	    &erase_size, NULL)) != 0)
1370 		goto fail1;
1371 
1372 	if (erase_size == 0) {
1373 		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1374 			goto fail2;
1375 	} else {
1376 		if (size % erase_size != 0) {
1377 			rc = EINVAL;
1378 			goto fail3;
1379 		}
1380 		while (size > 0) {
1381 			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1382 			    erase_size)) != 0)
1383 				goto fail4;
1384 			offset += erase_size;
1385 			size -= erase_size;
1386 		}
1387 	}
1388 
1389 	return (0);
1390 
1391 fail4:
1392 	EFSYS_PROBE(fail4);
1393 fail3:
1394 	EFSYS_PROBE(fail3);
1395 fail2:
1396 	EFSYS_PROBE(fail2);
1397 fail1:
1398 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1399 
1400 	return (rc);
1401 }
1402 
1403 	__checkReturn		efx_rc_t
hunt_nvram_partn_write(__in efx_nic_t * enp,__in unsigned int partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1404 hunt_nvram_partn_write(
1405 	__in			efx_nic_t *enp,
1406 	__in			unsigned int partn,
1407 	__in			unsigned int offset,
1408 	__out_bcount(size)	caddr_t data,
1409 	__in			size_t size)
1410 {
1411 	size_t chunk;
1412 	uint32_t write_size;
1413 	efx_rc_t rc;
1414 
1415 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1416 	    NULL, &write_size)) != 0)
1417 		goto fail1;
1418 
1419 	if (write_size != 0) {
1420 		/*
1421 		 * Check that the size is a multiple of the write chunk size if
1422 		 * the write chunk size is available.
1423 		 */
1424 		if (size % write_size != 0) {
1425 			rc = EINVAL;
1426 			goto fail2;
1427 		}
1428 	} else {
1429 		write_size = HUNTINGTON_NVRAM_CHUNK;
1430 	}
1431 
1432 	while (size > 0) {
1433 		chunk = MIN(size, write_size);
1434 
1435 		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
1436 			    data, chunk)) != 0) {
1437 			goto fail3;
1438 		}
1439 
1440 		size -= chunk;
1441 		data += chunk;
1442 		offset += chunk;
1443 	}
1444 
1445 	return (0);
1446 
1447 fail3:
1448 	EFSYS_PROBE(fail3);
1449 fail2:
1450 	EFSYS_PROBE(fail2);
1451 fail1:
1452 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1453 
1454 	return (rc);
1455 }
1456 
1457 				void
hunt_nvram_partn_unlock(__in efx_nic_t * enp,__in unsigned int partn)1458 hunt_nvram_partn_unlock(
1459 	__in			efx_nic_t *enp,
1460 	__in			unsigned int partn)
1461 {
1462 	boolean_t reboot;
1463 	efx_rc_t rc;
1464 
1465 	reboot = B_FALSE;
1466 	if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0)
1467 		goto fail1;
1468 
1469 	return;
1470 
1471 fail1:
1472 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1473 }
1474 
1475 	__checkReturn		efx_rc_t
1476 hunt_nvram_partn_set_version(
1477 	__in			efx_nic_t *enp,
1478 	__in			unsigned int partn,
1479 	__in_ecount(4)		uint16_t version[4])
1480 {
1481 	struct tlv_partition_version partn_version;
1482 	size_t size;
1483 	efx_rc_t rc;
1484 
1485 	/* Add or modify partition version TLV item */
1486 	partn_version.version_w = __CPU_TO_LE_16(version[0]);
1487 	partn_version.version_x = __CPU_TO_LE_16(version[1]);
1488 	partn_version.version_y = __CPU_TO_LE_16(version[2]);
1489 	partn_version.version_z = __CPU_TO_LE_16(version[3]);
1490 
1491 	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
1492 
1493 	/* Write the version number to all segments in the partition */
1494 	if ((rc = hunt_nvram_partn_write_segment_tlv(enp,
1495 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
1496 		    TLV_TAG_PARTITION_VERSION(partn),
1497 		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
1498 		goto fail1;
1499 
1500 	return (0);
1501 
1502 fail1:
1503 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1504 
1505 	return (rc);
1506 }
1507 
1508 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
1509 
1510 #if EFSYS_OPT_NVRAM
1511 
1512 typedef struct hunt_parttbl_entry_s {
1513 	unsigned int		partn;
1514 	unsigned int		port;
1515 	efx_nvram_type_t	nvtype;
1516 } hunt_parttbl_entry_t;
1517 
1518 /* Translate EFX NVRAM types to firmware partition types */
1519 static hunt_parttbl_entry_t hunt_parttbl[] = {
1520 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
1521 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
1522 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
1523 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
1524 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
1525 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
1526 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
1527 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
1528 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
1529 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
1530 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
1531 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
1532 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
1533 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
1534 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
1535 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
1536 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
1537 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
1538 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
1539 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
1540 	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
1541 	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
1542 	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
1543 	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
1544 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
1545 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
1546 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
1547 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP}
1548 };
1549 
1550 static	__checkReturn		hunt_parttbl_entry_t *
hunt_parttbl_entry(__in efx_nic_t * enp,__in efx_nvram_type_t type)1551 hunt_parttbl_entry(
1552 	__in			efx_nic_t *enp,
1553 	__in			efx_nvram_type_t type)
1554 {
1555 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1556 	hunt_parttbl_entry_t *entry;
1557 	int i;
1558 
1559 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
1560 
1561 	for (i = 0; i < EFX_ARRAY_SIZE(hunt_parttbl); i++) {
1562 		entry = &hunt_parttbl[i];
1563 
1564 		if (entry->port == emip->emi_port && entry->nvtype == type)
1565 			return (entry);
1566 	}
1567 
1568 	return (NULL);
1569 }
1570 
1571 
1572 #if EFSYS_OPT_DIAG
1573 
1574 	__checkReturn		efx_rc_t
hunt_nvram_test(__in efx_nic_t * enp)1575 hunt_nvram_test(
1576 	__in			efx_nic_t *enp)
1577 {
1578 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1579 	hunt_parttbl_entry_t *entry;
1580 	unsigned int npartns = 0;
1581 	uint32_t *partns = NULL;
1582 	size_t size;
1583 	int i;
1584 	unsigned int j;
1585 	efx_rc_t rc;
1586 
1587 	/* Find supported partitions */
1588 	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
1589 	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
1590 	if (partns == NULL) {
1591 		rc = ENOMEM;
1592 		goto fail1;
1593 	}
1594 
1595 	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
1596 		    &npartns)) != 0) {
1597 		goto fail2;
1598 	}
1599 
1600 	/*
1601 	 * Iterate over the list of supported partition types
1602 	 * applicable to *this* port
1603 	 */
1604 	for (i = 0; i < EFX_ARRAY_SIZE(hunt_parttbl); i++) {
1605 		entry = &hunt_parttbl[i];
1606 
1607 		if (entry->port != emip->emi_port)
1608 			continue;
1609 
1610 		for (j = 0; j < npartns; j++) {
1611 			if (entry->partn == partns[j]) {
1612 				rc = efx_mcdi_nvram_test(enp, entry->partn);
1613 				if (rc != 0)
1614 					goto fail3;
1615 			}
1616 		}
1617 	}
1618 
1619 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1620 	return (0);
1621 
1622 fail3:
1623 	EFSYS_PROBE(fail3);
1624 fail2:
1625 	EFSYS_PROBE(fail2);
1626 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
1627 fail1:
1628 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1629 	return (rc);
1630 }
1631 
1632 #endif	/* EFSYS_OPT_DIAG */
1633 
1634 	__checkReturn		efx_rc_t
hunt_nvram_size(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out size_t * sizep)1635 hunt_nvram_size(
1636 	__in			efx_nic_t *enp,
1637 	__in			efx_nvram_type_t type,
1638 	__out			size_t *sizep)
1639 {
1640 	hunt_parttbl_entry_t *entry;
1641 	uint32_t partn;
1642 	efx_rc_t rc;
1643 
1644 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1645 		rc = ENOTSUP;
1646 		goto fail1;
1647 	}
1648 	partn = entry->partn;
1649 
1650 	if ((rc = hunt_nvram_partn_size(enp, partn, sizep)) != 0)
1651 		goto fail2;
1652 
1653 	return (0);
1654 
1655 fail2:
1656 	EFSYS_PROBE(fail2);
1657 fail1:
1658 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1659 
1660 	*sizep = 0;
1661 
1662 	return (rc);
1663 }
1664 
1665 	__checkReturn		efx_rc_t
1666 hunt_nvram_get_version(
1667 	__in			efx_nic_t *enp,
1668 	__in			efx_nvram_type_t type,
1669 	__out			uint32_t *subtypep,
1670 	__out_ecount(4)		uint16_t version[4])
1671 {
1672 	hunt_parttbl_entry_t *entry;
1673 	uint32_t partn;
1674 	efx_rc_t rc;
1675 
1676 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1677 		rc = ENOTSUP;
1678 		goto fail1;
1679 	}
1680 	partn = entry->partn;
1681 
1682 	/* FIXME: get highest partn version from all ports */
1683 	/* FIXME: return partn description if available */
1684 
1685 	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
1686 		    version, NULL, 0)) != 0)
1687 		goto fail2;
1688 
1689 	return (0);
1690 
1691 fail2:
1692 	EFSYS_PROBE(fail2);
1693 fail1:
1694 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1695 
1696 	return (rc);
1697 }
1698 
1699 	__checkReturn		efx_rc_t
hunt_nvram_rw_start(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out size_t * chunk_sizep)1700 hunt_nvram_rw_start(
1701 	__in			efx_nic_t *enp,
1702 	__in			efx_nvram_type_t type,
1703 	__out			size_t *chunk_sizep)
1704 {
1705 	hunt_parttbl_entry_t *entry;
1706 	uint32_t partn;
1707 	efx_rc_t rc;
1708 
1709 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1710 		rc = ENOTSUP;
1711 		goto fail1;
1712 	}
1713 	partn = entry->partn;
1714 
1715 	if ((rc = hunt_nvram_partn_lock(enp, partn)) != 0)
1716 		goto fail2;
1717 
1718 	if (chunk_sizep != NULL)
1719 		*chunk_sizep = HUNTINGTON_NVRAM_CHUNK;
1720 
1721 	return (0);
1722 
1723 fail2:
1724 	EFSYS_PROBE(fail2);
1725 fail1:
1726 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1727 
1728 	return (rc);
1729 }
1730 
1731 	__checkReturn		efx_rc_t
hunt_nvram_read_chunk(__in efx_nic_t * enp,__in efx_nvram_type_t type,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1732 hunt_nvram_read_chunk(
1733 	__in			efx_nic_t *enp,
1734 	__in			efx_nvram_type_t type,
1735 	__in			unsigned int offset,
1736 	__out_bcount(size)	caddr_t data,
1737 	__in			size_t size)
1738 {
1739 	hunt_parttbl_entry_t *entry;
1740 	efx_rc_t rc;
1741 
1742 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1743 		rc = ENOTSUP;
1744 		goto fail1;
1745 	}
1746 
1747 	if ((rc = hunt_nvram_partn_read(enp, entry->partn,
1748 		    offset, data, size)) != 0)
1749 		goto fail2;
1750 
1751 	return (0);
1752 
1753 fail2:
1754 	EFSYS_PROBE(fail2);
1755 fail1:
1756 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1757 
1758 	return (rc);
1759 }
1760 
1761 	__checkReturn		efx_rc_t
hunt_nvram_erase(__in efx_nic_t * enp,__in efx_nvram_type_t type)1762 hunt_nvram_erase(
1763 	__in			efx_nic_t *enp,
1764 	__in			efx_nvram_type_t type)
1765 {
1766 	hunt_parttbl_entry_t *entry;
1767 	size_t size;
1768 	efx_rc_t rc;
1769 
1770 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1771 		rc = ENOTSUP;
1772 		goto fail1;
1773 	}
1774 
1775 	if ((rc = hunt_nvram_partn_size(enp, entry->partn, &size)) != 0)
1776 		goto fail2;
1777 
1778 	if ((rc = hunt_nvram_partn_erase(enp, entry->partn, 0, size)) != 0)
1779 		goto fail3;
1780 
1781 	return (0);
1782 
1783 fail3:
1784 	EFSYS_PROBE(fail3);
1785 fail2:
1786 	EFSYS_PROBE(fail2);
1787 fail1:
1788 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1789 
1790 	return (rc);
1791 }
1792 
1793 	__checkReturn		efx_rc_t
hunt_nvram_write_chunk(__in efx_nic_t * enp,__in efx_nvram_type_t type,__in unsigned int offset,__in_bcount (size)caddr_t data,__in size_t size)1794 hunt_nvram_write_chunk(
1795 	__in			efx_nic_t *enp,
1796 	__in			efx_nvram_type_t type,
1797 	__in			unsigned int offset,
1798 	__in_bcount(size)	caddr_t data,
1799 	__in			size_t size)
1800 {
1801 	hunt_parttbl_entry_t *entry;
1802 	efx_rc_t rc;
1803 
1804 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1805 		rc = ENOTSUP;
1806 		goto fail1;
1807 	}
1808 
1809 	if ((rc = hunt_nvram_partn_write(enp, entry->partn,
1810 		    offset, data, size)) != 0)
1811 		goto fail2;
1812 
1813 	return (0);
1814 
1815 fail2:
1816 	EFSYS_PROBE(fail2);
1817 fail1:
1818 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1819 
1820 	return (rc);
1821 }
1822 
1823 				void
hunt_nvram_rw_finish(__in efx_nic_t * enp,__in efx_nvram_type_t type)1824 hunt_nvram_rw_finish(
1825 	__in			efx_nic_t *enp,
1826 	__in			efx_nvram_type_t type)
1827 {
1828 	hunt_parttbl_entry_t *entry;
1829 
1830 	if ((entry = hunt_parttbl_entry(enp, type)) != NULL)
1831 		hunt_nvram_partn_unlock(enp, entry->partn);
1832 }
1833 
1834 	__checkReturn		efx_rc_t
1835 hunt_nvram_set_version(
1836 	__in			efx_nic_t *enp,
1837 	__in			efx_nvram_type_t type,
1838 	__in_ecount(4)		uint16_t version[4])
1839 {
1840 	hunt_parttbl_entry_t *entry;
1841 	unsigned int partn;
1842 	efx_rc_t rc;
1843 
1844 	if ((entry = hunt_parttbl_entry(enp, type)) == NULL) {
1845 		rc = ENOTSUP;
1846 		goto fail1;
1847 	}
1848 	partn = entry->partn;
1849 
1850 	if ((rc = hunt_nvram_partn_set_version(enp, partn, version)) != 0)
1851 		goto fail2;
1852 
1853 	return (0);
1854 
1855 fail2:
1856 	EFSYS_PROBE(fail2);
1857 
1858 fail1:
1859 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1860 
1861 	return (rc);
1862 }
1863 
1864 #endif	/* EFSYS_OPT_NVRAM */
1865 
1866 #endif	/* EFSYS_OPT_HUNTINGTON */
1867