1 /*-
2  * Copyright (c) 2012-2016 Solarflare Communications Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright notice,
11  *    this list of conditions and the following disclaimer in the documentation
12  *    and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * The views and conclusions contained in the software and documentation are
27  * those of the authors and should not be interpreted as representing official
28  * policies, either expressed or implied, of the FreeBSD Project.
29  */
30 
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/ef10_nvram.c 342512 2018-12-26 10:21:40Z arybchik $");
33 
34 #include "efx.h"
35 #include "efx_impl.h"
36 
37 #if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD
38 
39 #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
40 
41 #include "ef10_tlv_layout.h"
42 
43 /* Cursor for TLV partition format */
44 typedef struct tlv_cursor_s {
45 	uint32_t	*block;			/* Base of data block */
46 	uint32_t	*current;		/* Cursor position */
47 	uint32_t	*end;			/* End tag position */
48 	uint32_t	*limit;			/* Last dword of data block */
49 } tlv_cursor_t;
50 
51 typedef struct nvram_partition_s {
52 	uint16_t type;
53 	uint8_t chip_select;
54 	uint8_t flags;
55 	/*
56 	 * The full length of the NVRAM partition.
57 	 * This is different from tlv_partition_header.total_length,
58 	 *  which can be smaller.
59 	 */
60 	uint32_t length;
61 	uint32_t erase_size;
62 	uint32_t *data;
63 	tlv_cursor_t tlv_cursor;
64 } nvram_partition_t;
65 
66 
67 static	__checkReturn		efx_rc_t
68 tlv_validate_state(
69 	__inout			tlv_cursor_t *cursor);
70 
71 
72 static				void
tlv_init_block(__out uint32_t * block)73 tlv_init_block(
74 	__out	uint32_t	*block)
75 {
76 	*block = __CPU_TO_LE_32(TLV_TAG_END);
77 }
78 
79 static				uint32_t
tlv_tag(__in tlv_cursor_t * cursor)80 tlv_tag(
81 	__in	tlv_cursor_t	*cursor)
82 {
83 	uint32_t dword, tag;
84 
85 	dword = cursor->current[0];
86 	tag = __LE_TO_CPU_32(dword);
87 
88 	return (tag);
89 }
90 
91 static				size_t
tlv_length(__in tlv_cursor_t * cursor)92 tlv_length(
93 	__in	tlv_cursor_t	*cursor)
94 {
95 	uint32_t dword, length;
96 
97 	if (tlv_tag(cursor) == TLV_TAG_END)
98 		return (0);
99 
100 	dword = cursor->current[1];
101 	length = __LE_TO_CPU_32(dword);
102 
103 	return ((size_t)length);
104 }
105 
106 static				uint8_t *
tlv_value(__in tlv_cursor_t * cursor)107 tlv_value(
108 	__in	tlv_cursor_t	*cursor)
109 {
110 	if (tlv_tag(cursor) == TLV_TAG_END)
111 		return (NULL);
112 
113 	return ((uint8_t *)(&cursor->current[2]));
114 }
115 
116 static				uint8_t *
tlv_item(__in tlv_cursor_t * cursor)117 tlv_item(
118 	__in	tlv_cursor_t	*cursor)
119 {
120 	if (tlv_tag(cursor) == TLV_TAG_END)
121 		return (NULL);
122 
123 	return ((uint8_t *)cursor->current);
124 }
125 
126 /*
127  * TLV item DWORD length is tag + length + value (rounded up to DWORD)
128  * equivalent to tlv_n_words_for_len in mc-comms tlv.c
129  */
130 #define	TLV_DWORD_COUNT(length) \
131 	(1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
132 
133 
134 static				uint32_t *
tlv_next_item_ptr(__in tlv_cursor_t * cursor)135 tlv_next_item_ptr(
136 	__in	tlv_cursor_t	*cursor)
137 {
138 	uint32_t length;
139 
140 	length = tlv_length(cursor);
141 
142 	return (cursor->current + TLV_DWORD_COUNT(length));
143 }
144 
145 static	__checkReturn		efx_rc_t
tlv_advance(__inout tlv_cursor_t * cursor)146 tlv_advance(
147 	__inout	tlv_cursor_t	*cursor)
148 {
149 	efx_rc_t rc;
150 
151 	if ((rc = tlv_validate_state(cursor)) != 0)
152 		goto fail1;
153 
154 	if (cursor->current == cursor->end) {
155 		/* No more tags after END tag */
156 		cursor->current = NULL;
157 		rc = ENOENT;
158 		goto fail2;
159 	}
160 
161 	/* Advance to next item and validate */
162 	cursor->current = tlv_next_item_ptr(cursor);
163 
164 	if ((rc = tlv_validate_state(cursor)) != 0)
165 		goto fail3;
166 
167 	return (0);
168 
169 fail3:
170 	EFSYS_PROBE(fail3);
171 fail2:
172 	EFSYS_PROBE(fail2);
173 fail1:
174 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
175 
176 	return (rc);
177 }
178 
179 static				efx_rc_t
tlv_rewind(__in tlv_cursor_t * cursor)180 tlv_rewind(
181 	__in	tlv_cursor_t	*cursor)
182 {
183 	efx_rc_t rc;
184 
185 	cursor->current = cursor->block;
186 
187 	if ((rc = tlv_validate_state(cursor)) != 0)
188 		goto fail1;
189 
190 	return (0);
191 
192 fail1:
193 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
194 
195 	return (rc);
196 }
197 
198 static				efx_rc_t
tlv_find(__inout tlv_cursor_t * cursor,__in uint32_t tag)199 tlv_find(
200 	__inout	tlv_cursor_t	*cursor,
201 	__in	uint32_t	tag)
202 {
203 	efx_rc_t rc;
204 
205 	rc = tlv_rewind(cursor);
206 	while (rc == 0) {
207 		if (tlv_tag(cursor) == tag)
208 			break;
209 
210 		rc = tlv_advance(cursor);
211 	}
212 	return (rc);
213 }
214 
215 static	__checkReturn		efx_rc_t
tlv_validate_state(__inout tlv_cursor_t * cursor)216 tlv_validate_state(
217 	__inout	tlv_cursor_t	*cursor)
218 {
219 	efx_rc_t rc;
220 
221 	/* Check cursor position */
222 	if (cursor->current < cursor->block) {
223 		rc = EINVAL;
224 		goto fail1;
225 	}
226 	if (cursor->current > cursor->limit) {
227 		rc = EINVAL;
228 		goto fail2;
229 	}
230 
231 	if (tlv_tag(cursor) != TLV_TAG_END) {
232 		/* Check current item has space for tag and length */
233 		if (cursor->current > (cursor->limit - 2)) {
234 			cursor->current = NULL;
235 			rc = EFAULT;
236 			goto fail3;
237 		}
238 
239 		/* Check we have value data for current item and another tag */
240 		if (tlv_next_item_ptr(cursor) > (cursor->limit - 1)) {
241 			cursor->current = NULL;
242 			rc = EFAULT;
243 			goto fail4;
244 		}
245 	}
246 
247 	return (0);
248 
249 fail4:
250 	EFSYS_PROBE(fail4);
251 fail3:
252 	EFSYS_PROBE(fail3);
253 fail2:
254 	EFSYS_PROBE(fail2);
255 fail1:
256 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
257 
258 	return (rc);
259 }
260 
261 static				efx_rc_t
tlv_init_cursor(__out tlv_cursor_t * cursor,__in uint32_t * block,__in uint32_t * limit,__in uint32_t * current)262 tlv_init_cursor(
263 	__out	tlv_cursor_t	*cursor,
264 	__in	uint32_t	*block,
265 	__in	uint32_t	*limit,
266 	__in	uint32_t	*current)
267 {
268 	cursor->block	= block;
269 	cursor->limit	= limit;
270 
271 	cursor->current	= current;
272 	cursor->end	= NULL;
273 
274 	return (tlv_validate_state(cursor));
275 }
276 
277 static	__checkReturn		efx_rc_t
tlv_init_cursor_from_size(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size)278 tlv_init_cursor_from_size(
279 	__out	tlv_cursor_t	*cursor,
280 	__in_bcount(size)
281 		uint8_t		*block,
282 	__in	size_t		size)
283 {
284 	uint32_t *limit;
285 	limit = (uint32_t *)(block + size - sizeof (uint32_t));
286 	return (tlv_init_cursor(cursor, (uint32_t *)block,
287 		limit, (uint32_t *)block));
288 }
289 
290 static	__checkReturn		efx_rc_t
tlv_init_cursor_at_offset(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size,__in size_t offset)291 tlv_init_cursor_at_offset(
292 	__out	tlv_cursor_t	*cursor,
293 	__in_bcount(size)
294 		uint8_t		*block,
295 	__in	size_t		size,
296 	__in	size_t		offset)
297 {
298 	uint32_t *limit;
299 	uint32_t *current;
300 	limit = (uint32_t *)(block + size - sizeof (uint32_t));
301 	current = (uint32_t *)(block + offset);
302 	return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
303 }
304 
305 static	__checkReturn		efx_rc_t
tlv_require_end(__inout tlv_cursor_t * cursor)306 tlv_require_end(
307 	__inout	tlv_cursor_t	*cursor)
308 {
309 	uint32_t *pos;
310 	efx_rc_t rc;
311 
312 	if (cursor->end == NULL) {
313 		pos = cursor->current;
314 		if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
315 			goto fail1;
316 
317 		cursor->end = cursor->current;
318 		cursor->current = pos;
319 	}
320 
321 	return (0);
322 
323 fail1:
324 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
325 
326 	return (rc);
327 }
328 
329 static				size_t
tlv_block_length_used(__inout tlv_cursor_t * cursor)330 tlv_block_length_used(
331 	__inout	tlv_cursor_t	*cursor)
332 {
333 	efx_rc_t rc;
334 
335 	if ((rc = tlv_validate_state(cursor)) != 0)
336 		goto fail1;
337 
338 	if ((rc = tlv_require_end(cursor)) != 0)
339 		goto fail2;
340 
341 	/* Return space used (including the END tag) */
342 	return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
343 
344 fail2:
345 	EFSYS_PROBE(fail2);
346 fail1:
347 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
348 
349 	return (0);
350 }
351 
352 static		uint32_t *
tlv_last_segment_end(__in tlv_cursor_t * cursor)353 tlv_last_segment_end(
354 	__in	tlv_cursor_t *cursor)
355 {
356 	tlv_cursor_t segment_cursor;
357 	uint32_t *last_segment_end = cursor->block;
358 	uint32_t *segment_start = cursor->block;
359 
360 	/*
361 	 * Go through each segment and check that it has an end tag. If there
362 	 * is no end tag then the previous segment was the last valid one,
363 	 * so return the pointer to its end tag.
364 	 */
365 	for (;;) {
366 		if (tlv_init_cursor(&segment_cursor, segment_start,
367 		    cursor->limit, segment_start) != 0)
368 			break;
369 		if (tlv_require_end(&segment_cursor) != 0)
370 			break;
371 		last_segment_end = segment_cursor.end;
372 		segment_start = segment_cursor.end + 1;
373 	}
374 
375 	return (last_segment_end);
376 }
377 
378 
379 static				uint32_t *
tlv_write(__in tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)380 tlv_write(
381 	__in			tlv_cursor_t *cursor,
382 	__in			uint32_t tag,
383 	__in_bcount(size)	uint8_t *data,
384 	__in			size_t size)
385 {
386 	uint32_t len = size;
387 	uint32_t *ptr;
388 
389 	ptr = cursor->current;
390 
391 	*ptr++ = __CPU_TO_LE_32(tag);
392 	*ptr++ = __CPU_TO_LE_32(len);
393 
394 	if (len > 0) {
395 		ptr[(len - 1) / sizeof (uint32_t)] = 0;
396 		memcpy(ptr, data, len);
397 		ptr += P2ROUNDUP(len, sizeof (uint32_t)) / sizeof (*ptr);
398 	}
399 
400 	return (ptr);
401 }
402 
403 static	__checkReturn		efx_rc_t
tlv_insert(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)404 tlv_insert(
405 	__inout	tlv_cursor_t	*cursor,
406 	__in	uint32_t	tag,
407 	__in_bcount(size)
408 		uint8_t		*data,
409 	__in	size_t		size)
410 {
411 	unsigned int delta;
412 	uint32_t *last_segment_end;
413 	efx_rc_t rc;
414 
415 	if ((rc = tlv_validate_state(cursor)) != 0)
416 		goto fail1;
417 
418 	if ((rc = tlv_require_end(cursor)) != 0)
419 		goto fail2;
420 
421 	if (tag == TLV_TAG_END) {
422 		rc = EINVAL;
423 		goto fail3;
424 	}
425 
426 	last_segment_end = tlv_last_segment_end(cursor);
427 
428 	delta = TLV_DWORD_COUNT(size);
429 	if (last_segment_end + 1 + delta > cursor->limit) {
430 		rc = ENOSPC;
431 		goto fail4;
432 	}
433 
434 	/* Move data up: new space at cursor->current */
435 	memmove(cursor->current + delta, cursor->current,
436 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
437 
438 	/* Adjust the end pointer */
439 	cursor->end += delta;
440 
441 	/* Write new TLV item */
442 	tlv_write(cursor, tag, data, size);
443 
444 	return (0);
445 
446 fail4:
447 	EFSYS_PROBE(fail4);
448 fail3:
449 	EFSYS_PROBE(fail3);
450 fail2:
451 	EFSYS_PROBE(fail2);
452 fail1:
453 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
454 
455 	return (rc);
456 }
457 
458 static	__checkReturn		efx_rc_t
tlv_delete(__inout tlv_cursor_t * cursor)459 tlv_delete(
460 	__inout	tlv_cursor_t	*cursor)
461 {
462 	unsigned int delta;
463 	uint32_t *last_segment_end;
464 	efx_rc_t rc;
465 
466 	if ((rc = tlv_validate_state(cursor)) != 0)
467 		goto fail1;
468 
469 	if (tlv_tag(cursor) == TLV_TAG_END) {
470 		rc = EINVAL;
471 		goto fail2;
472 	}
473 
474 	delta = TLV_DWORD_COUNT(tlv_length(cursor));
475 
476 	if ((rc = tlv_require_end(cursor)) != 0)
477 		goto fail3;
478 
479 	last_segment_end = tlv_last_segment_end(cursor);
480 
481 	/* Shuffle things down, destroying the item at cursor->current */
482 	memmove(cursor->current, cursor->current + delta,
483 	    (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
484 	/* Zero the new space at the end of the TLV chain */
485 	memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
486 	/* Adjust the end pointer */
487 	cursor->end -= delta;
488 
489 	return (0);
490 
491 fail3:
492 	EFSYS_PROBE(fail3);
493 fail2:
494 	EFSYS_PROBE(fail2);
495 fail1:
496 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
497 
498 	return (rc);
499 }
500 
501 static	__checkReturn		efx_rc_t
tlv_modify(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)502 tlv_modify(
503 	__inout	tlv_cursor_t	*cursor,
504 	__in	uint32_t	tag,
505 	__in_bcount(size)
506 		uint8_t		*data,
507 	__in	size_t		size)
508 {
509 	uint32_t *pos;
510 	unsigned int old_ndwords;
511 	unsigned int new_ndwords;
512 	unsigned int delta;
513 	uint32_t *last_segment_end;
514 	efx_rc_t rc;
515 
516 	if ((rc = tlv_validate_state(cursor)) != 0)
517 		goto fail1;
518 
519 	if (tlv_tag(cursor) == TLV_TAG_END) {
520 		rc = EINVAL;
521 		goto fail2;
522 	}
523 	if (tlv_tag(cursor) != tag) {
524 		rc = EINVAL;
525 		goto fail3;
526 	}
527 
528 	old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
529 	new_ndwords = TLV_DWORD_COUNT(size);
530 
531 	if ((rc = tlv_require_end(cursor)) != 0)
532 		goto fail4;
533 
534 	last_segment_end = tlv_last_segment_end(cursor);
535 
536 	if (new_ndwords > old_ndwords) {
537 		/* Expand space used for TLV item */
538 		delta = new_ndwords - old_ndwords;
539 		pos = cursor->current + old_ndwords;
540 
541 		if (last_segment_end + 1 + delta > cursor->limit) {
542 			rc = ENOSPC;
543 			goto fail5;
544 		}
545 
546 		/* Move up: new space at (cursor->current + old_ndwords) */
547 		memmove(pos + delta, pos,
548 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
549 
550 		/* Adjust the end pointer */
551 		cursor->end += delta;
552 
553 	} else if (new_ndwords < old_ndwords) {
554 		/* Shrink space used for TLV item */
555 		delta = old_ndwords - new_ndwords;
556 		pos = cursor->current + new_ndwords;
557 
558 		/* Move down: remove words at (cursor->current + new_ndwords) */
559 		memmove(pos, pos + delta,
560 		    (last_segment_end + 1 - pos) * sizeof (uint32_t));
561 
562 		/* Zero the new space at the end of the TLV chain */
563 		memset(last_segment_end + 1 - delta, 0,
564 		    delta * sizeof (uint32_t));
565 
566 		/* Adjust the end pointer */
567 		cursor->end -= delta;
568 	}
569 
570 	/* Write new data */
571 	tlv_write(cursor, tag, data, size);
572 
573 	return (0);
574 
575 fail5:
576 	EFSYS_PROBE(fail5);
577 fail4:
578 	EFSYS_PROBE(fail4);
579 fail3:
580 	EFSYS_PROBE(fail3);
581 fail2:
582 	EFSYS_PROBE(fail2);
583 fail1:
584 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
585 
586 	return (rc);
587 }
588 
checksum_tlv_partition(__in nvram_partition_t * partition)589 static uint32_t checksum_tlv_partition(
590 	__in	nvram_partition_t *partition)
591 {
592 	tlv_cursor_t *cursor;
593 	uint32_t *ptr;
594 	uint32_t *end;
595 	uint32_t csum;
596 	size_t len;
597 
598 	cursor = &partition->tlv_cursor;
599 	len = tlv_block_length_used(cursor);
600 	EFSYS_ASSERT3U((len & 3), ==, 0);
601 
602 	csum = 0;
603 	ptr = partition->data;
604 	end = &ptr[len >> 2];
605 
606 	while (ptr < end)
607 		csum += __LE_TO_CPU_32(*ptr++);
608 
609 	return (csum);
610 }
611 
612 static	__checkReturn		efx_rc_t
tlv_update_partition_len_and_cks(__in tlv_cursor_t * cursor)613 tlv_update_partition_len_and_cks(
614 	__in	tlv_cursor_t *cursor)
615 {
616 	efx_rc_t rc;
617 	nvram_partition_t partition;
618 	struct tlv_partition_header *header;
619 	struct tlv_partition_trailer *trailer;
620 	size_t new_len;
621 
622 	/*
623 	 * We just modified the partition, so the total length may not be
624 	 * valid. Don't use tlv_find(), which performs some sanity checks
625 	 * that may fail here.
626 	 */
627 	partition.data = cursor->block;
628 	memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
629 	header = (struct tlv_partition_header *)partition.data;
630 	/* Sanity check. */
631 	if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
632 		rc = EFAULT;
633 		goto fail1;
634 	}
635 	new_len =  tlv_block_length_used(&partition.tlv_cursor);
636 	if (new_len == 0) {
637 		rc = EFAULT;
638 		goto fail2;
639 	}
640 	header->total_length = __CPU_TO_LE_32(new_len);
641 	/* Ensure the modified partition always has a new generation count. */
642 	header->generation = __CPU_TO_LE_32(
643 	    __LE_TO_CPU_32(header->generation) + 1);
644 
645 	trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
646 	    new_len - sizeof (*trailer) - sizeof (uint32_t));
647 	trailer->generation = header->generation;
648 	trailer->checksum = __CPU_TO_LE_32(
649 	    __LE_TO_CPU_32(trailer->checksum) -
650 	    checksum_tlv_partition(&partition));
651 
652 	return (0);
653 
654 fail2:
655 	EFSYS_PROBE(fail2);
656 fail1:
657 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
658 
659 	return (rc);
660 }
661 
662 /* Validate buffer contents (before writing to flash) */
663 	__checkReturn		efx_rc_t
ef10_nvram_buffer_validate(__in efx_nic_t * enp,__in uint32_t partn,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)664 ef10_nvram_buffer_validate(
665 	__in			efx_nic_t *enp,
666 	__in			uint32_t partn,
667 	__in_bcount(partn_size)	caddr_t partn_data,
668 	__in			size_t partn_size)
669 {
670 	tlv_cursor_t cursor;
671 	struct tlv_partition_header *header;
672 	struct tlv_partition_trailer *trailer;
673 	size_t total_length;
674 	uint32_t cksum;
675 	int pos;
676 	efx_rc_t rc;
677 
678 	_NOTE(ARGUNUSED(enp, partn))
679 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
680 
681 	if ((partn_data == NULL) || (partn_size == 0)) {
682 		rc = EINVAL;
683 		goto fail1;
684 	}
685 
686 	/* The partition header must be the first item (at offset zero) */
687 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
688 		    partn_size)) != 0) {
689 		rc = EFAULT;
690 		goto fail2;
691 	}
692 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
693 		rc = EINVAL;
694 		goto fail3;
695 	}
696 	header = (struct tlv_partition_header *)tlv_item(&cursor);
697 
698 	/* Check TLV partition length (includes the END tag) */
699 	total_length = __LE_TO_CPU_32(header->total_length);
700 	if (total_length > partn_size) {
701 		rc = EFBIG;
702 		goto fail4;
703 	}
704 
705 	/* Check partition ends with PARTITION_TRAILER and END tags */
706 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
707 		rc = EINVAL;
708 		goto fail5;
709 	}
710 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
711 
712 	if ((rc = tlv_advance(&cursor)) != 0) {
713 		rc = EINVAL;
714 		goto fail6;
715 	}
716 	if (tlv_tag(&cursor) != TLV_TAG_END) {
717 		rc = EINVAL;
718 		goto fail7;
719 	}
720 
721 	/* Check generation counts are consistent */
722 	if (trailer->generation != header->generation) {
723 		rc = EINVAL;
724 		goto fail8;
725 	}
726 
727 	/* Verify partition checksum */
728 	cksum = 0;
729 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
730 		cksum += *((uint32_t *)(partn_data + pos));
731 	}
732 	if (cksum != 0) {
733 		rc = EINVAL;
734 		goto fail9;
735 	}
736 
737 	return (0);
738 
739 fail9:
740 	EFSYS_PROBE(fail9);
741 fail8:
742 	EFSYS_PROBE(fail8);
743 fail7:
744 	EFSYS_PROBE(fail7);
745 fail6:
746 	EFSYS_PROBE(fail6);
747 fail5:
748 	EFSYS_PROBE(fail5);
749 fail4:
750 	EFSYS_PROBE(fail4);
751 fail3:
752 	EFSYS_PROBE(fail3);
753 fail2:
754 	EFSYS_PROBE(fail2);
755 fail1:
756 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
757 
758 	return (rc);
759 }
760 
761 
762 
763 	__checkReturn		efx_rc_t
ef10_nvram_buffer_create(__in efx_nic_t * enp,__in uint16_t partn_type,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)764 ef10_nvram_buffer_create(
765 	__in			efx_nic_t *enp,
766 	__in			uint16_t partn_type,
767 	__in_bcount(partn_size)	caddr_t partn_data,
768 	__in			size_t partn_size)
769 {
770 	uint32_t *buf = (uint32_t *)partn_data;
771 	efx_rc_t rc;
772 	tlv_cursor_t cursor;
773 	struct tlv_partition_header header;
774 	struct tlv_partition_trailer trailer;
775 
776 	unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
777 	    sizeof (struct tlv_partition_trailer);
778 	if (partn_size < min_buf_size) {
779 		rc = EINVAL;
780 		goto fail1;
781 	}
782 
783 	memset(buf, 0xff, partn_size);
784 
785 	tlv_init_block(buf);
786 	if ((rc = tlv_init_cursor(&cursor, buf,
787 	    (uint32_t *)((uint8_t *)buf + partn_size),
788 	    buf)) != 0) {
789 		goto fail2;
790 	}
791 
792 	header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
793 	header.length = __CPU_TO_LE_32(sizeof (header) - 8);
794 	header.type_id = __CPU_TO_LE_16(partn_type);
795 	header.preset = 0;
796 	header.generation = __CPU_TO_LE_32(1);
797 	header.total_length = 0;  /* This will be fixed below. */
798 	if ((rc = tlv_insert(
799 	    &cursor, TLV_TAG_PARTITION_HEADER,
800 	    (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
801 		goto fail3;
802 	if ((rc = tlv_advance(&cursor)) != 0)
803 		goto fail4;
804 
805 	trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
806 	trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
807 	trailer.generation = header.generation;
808 	trailer.checksum = 0;  /* This will be fixed below. */
809 	if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
810 	    (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
811 		goto fail5;
812 
813 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
814 		goto fail6;
815 
816 	/* Check that the partition is valid. */
817 	if ((rc = ef10_nvram_buffer_validate(enp, partn_type,
818 	    partn_data, partn_size)) != 0)
819 		goto fail7;
820 
821 	return (0);
822 
823 fail7:
824 	EFSYS_PROBE(fail7);
825 fail6:
826 	EFSYS_PROBE(fail6);
827 fail5:
828 	EFSYS_PROBE(fail5);
829 fail4:
830 	EFSYS_PROBE(fail4);
831 fail3:
832 	EFSYS_PROBE(fail3);
833 fail2:
834 	EFSYS_PROBE(fail2);
835 fail1:
836 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
837 
838 	return (rc);
839 }
840 
841 static			uint32_t
byte_offset(__in uint32_t * position,__in uint32_t * base)842 byte_offset(
843 	__in		uint32_t *position,
844 	__in		uint32_t *base)
845 {
846 	return (uint32_t)((uint8_t *)position - (uint8_t *)base);
847 }
848 
849 	__checkReturn		efx_rc_t
ef10_nvram_buffer_find_item_start(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__out uint32_t * startp)850 ef10_nvram_buffer_find_item_start(
851 	__in_bcount(buffer_size)
852 				caddr_t bufferp,
853 	__in			size_t buffer_size,
854 	__out			uint32_t *startp)
855 {
856 	/* Read past partition header to find start address of the first key */
857 	tlv_cursor_t cursor;
858 	efx_rc_t rc;
859 
860 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
861 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
862 			buffer_size)) != 0) {
863 		rc = EFAULT;
864 		goto fail1;
865 	}
866 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
867 		rc = EINVAL;
868 		goto fail2;
869 	}
870 
871 	if ((rc = tlv_advance(&cursor)) != 0) {
872 		rc = EINVAL;
873 		goto fail3;
874 	}
875 	*startp = byte_offset(cursor.current, cursor.block);
876 
877 	if ((rc = tlv_require_end(&cursor)) != 0)
878 		goto fail4;
879 
880 	return (0);
881 
882 fail4:
883 	EFSYS_PROBE(fail4);
884 fail3:
885 	EFSYS_PROBE(fail3);
886 fail2:
887 	EFSYS_PROBE(fail2);
888 fail1:
889 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
890 
891 	return (rc);
892 }
893 
894 	__checkReturn		efx_rc_t
ef10_nvram_buffer_find_end(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * endp)895 ef10_nvram_buffer_find_end(
896 	__in_bcount(buffer_size)
897 				caddr_t bufferp,
898 	__in			size_t buffer_size,
899 	__in			uint32_t offset,
900 	__out			uint32_t *endp)
901 {
902 	/* Read to end of partition */
903 	tlv_cursor_t cursor;
904 	efx_rc_t rc;
905 	uint32_t *segment_used;
906 
907 	_NOTE(ARGUNUSED(offset))
908 
909 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
910 			buffer_size)) != 0) {
911 		rc = EFAULT;
912 		goto fail1;
913 	}
914 
915 	segment_used = cursor.block;
916 
917 	/*
918 	 * Go through each segment and check that it has an end tag. If there
919 	 * is no end tag then the previous segment was the last valid one,
920 	 * so return the used space including that end tag.
921 	 */
922 	while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
923 		if (tlv_require_end(&cursor) != 0) {
924 			if (segment_used == cursor.block) {
925 				/*
926 				 * First segment is corrupt, so there is
927 				 * no valid data in partition.
928 				 */
929 				rc = EINVAL;
930 				goto fail2;
931 			}
932 			break;
933 		}
934 		segment_used = cursor.end + 1;
935 
936 		cursor.current = segment_used;
937 	}
938 	/* Return space used (including the END tag) */
939 	*endp = (segment_used - cursor.block) * sizeof (uint32_t);
940 
941 	return (0);
942 
943 fail2:
944 	EFSYS_PROBE(fail2);
945 fail1:
946 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
947 
948 	return (rc);
949 }
950 
951 	__checkReturn	__success(return != B_FALSE)	boolean_t
ef10_nvram_buffer_find_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * startp,__out uint32_t * lengthp)952 ef10_nvram_buffer_find_item(
953 	__in_bcount(buffer_size)
954 				caddr_t bufferp,
955 	__in			size_t buffer_size,
956 	__in			uint32_t offset,
957 	__out			uint32_t *startp,
958 	__out			uint32_t *lengthp)
959 {
960 	/* Find TLV at offset and return key start and length */
961 	tlv_cursor_t cursor;
962 	uint8_t *key;
963 	uint32_t tag;
964 
965 	if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
966 			buffer_size, offset) != 0) {
967 		return (B_FALSE);
968 	}
969 
970 	while ((key = tlv_item(&cursor)) != NULL) {
971 		tag = tlv_tag(&cursor);
972 		if (tag == TLV_TAG_PARTITION_HEADER ||
973 		    tag == TLV_TAG_PARTITION_TRAILER) {
974 			if (tlv_advance(&cursor) != 0) {
975 				break;
976 			}
977 			continue;
978 		}
979 		*startp = byte_offset(cursor.current, cursor.block);
980 		*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
981 		    cursor.current);
982 		return (B_TRUE);
983 	}
984 
985 	return (B_FALSE);
986 }
987 
988 	__checkReturn		efx_rc_t
ef10_nvram_buffer_get_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__out_bcount_part (item_max_size,* lengthp)caddr_t itemp,__in size_t item_max_size,__out uint32_t * lengthp)989 ef10_nvram_buffer_get_item(
990 	__in_bcount(buffer_size)
991 				caddr_t bufferp,
992 	__in			size_t buffer_size,
993 	__in			uint32_t offset,
994 	__in			uint32_t length,
995 	__out_bcount_part(item_max_size, *lengthp)
996 				caddr_t itemp,
997 	__in			size_t item_max_size,
998 	__out			uint32_t *lengthp)
999 {
1000 	efx_rc_t rc;
1001 	tlv_cursor_t cursor;
1002 	uint32_t item_length;
1003 
1004 	if (item_max_size < length) {
1005 		rc = ENOSPC;
1006 		goto fail1;
1007 	}
1008 
1009 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1010 			buffer_size, offset)) != 0) {
1011 		goto fail2;
1012 	}
1013 
1014 	item_length = tlv_length(&cursor);
1015 	if (length < item_length) {
1016 		rc = ENOSPC;
1017 		goto fail3;
1018 	}
1019 	memcpy(itemp, tlv_value(&cursor), item_length);
1020 
1021 	*lengthp = item_length;
1022 
1023 	return (0);
1024 
1025 fail3:
1026 	EFSYS_PROBE(fail3);
1027 fail2:
1028 	EFSYS_PROBE(fail2);
1029 fail1:
1030 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1031 
1032 	return (rc);
1033 }
1034 
1035 	__checkReturn		efx_rc_t
ef10_nvram_buffer_insert_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in_bcount (length)caddr_t keyp,__in uint32_t length,__out uint32_t * lengthp)1036 ef10_nvram_buffer_insert_item(
1037 	__in_bcount(buffer_size)
1038 				caddr_t bufferp,
1039 	__in			size_t buffer_size,
1040 	__in			uint32_t offset,
1041 	__in_bcount(length)	caddr_t keyp,
1042 	__in			uint32_t length,
1043 	__out			uint32_t *lengthp)
1044 {
1045 	efx_rc_t rc;
1046 	tlv_cursor_t cursor;
1047 
1048 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1049 			buffer_size, offset)) != 0) {
1050 		goto fail1;
1051 	}
1052 
1053 	rc = tlv_insert(&cursor, TLV_TAG_LICENSE, (uint8_t *)keyp, length);
1054 
1055 	if (rc != 0) {
1056 		goto fail2;
1057 	}
1058 
1059 	*lengthp = byte_offset(tlv_next_item_ptr(&cursor),
1060 		    cursor.current);
1061 
1062 	return (0);
1063 
1064 fail2:
1065 	EFSYS_PROBE(fail2);
1066 fail1:
1067 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1068 
1069 	return (rc);
1070 }
1071 
1072 	__checkReturn		efx_rc_t
ef10_nvram_buffer_delete_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__in uint32_t end)1073 ef10_nvram_buffer_delete_item(
1074 	__in_bcount(buffer_size)
1075 				caddr_t bufferp,
1076 	__in			size_t buffer_size,
1077 	__in			uint32_t offset,
1078 	__in			uint32_t length,
1079 	__in			uint32_t end)
1080 {
1081 	efx_rc_t rc;
1082 	tlv_cursor_t cursor;
1083 
1084 	_NOTE(ARGUNUSED(length, end))
1085 
1086 	if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
1087 			buffer_size, offset)) != 0) {
1088 		goto fail1;
1089 	}
1090 
1091 	if ((rc = tlv_delete(&cursor)) != 0)
1092 		goto fail2;
1093 
1094 	return (0);
1095 
1096 fail2:
1097 	EFSYS_PROBE(fail2);
1098 fail1:
1099 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1100 
1101 	return (rc);
1102 }
1103 
1104 	__checkReturn		efx_rc_t
ef10_nvram_buffer_finish(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size)1105 ef10_nvram_buffer_finish(
1106 	__in_bcount(buffer_size)
1107 				caddr_t bufferp,
1108 	__in			size_t buffer_size)
1109 {
1110 	efx_rc_t rc;
1111 	tlv_cursor_t cursor;
1112 
1113 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
1114 			buffer_size)) != 0) {
1115 		rc = EFAULT;
1116 		goto fail1;
1117 	}
1118 
1119 	if ((rc = tlv_require_end(&cursor)) != 0)
1120 		goto fail2;
1121 
1122 	if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
1123 		goto fail3;
1124 
1125 	return (0);
1126 
1127 fail3:
1128 	EFSYS_PROBE(fail3);
1129 fail2:
1130 	EFSYS_PROBE(fail2);
1131 fail1:
1132 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1133 
1134 	return (rc);
1135 }
1136 
1137 
1138 
1139 /*
1140  * Read and validate a segment from a partition. A segment is a complete
1141  * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
1142  * be multiple segments in a partition, so seg_offset allows segments
1143  * beyond the first to be read.
1144  */
1145 static	__checkReturn			efx_rc_t
ef10_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)1146 ef10_nvram_read_tlv_segment(
1147 	__in				efx_nic_t *enp,
1148 	__in				uint32_t partn,
1149 	__in				size_t seg_offset,
1150 	__in_bcount(max_seg_size)	caddr_t seg_data,
1151 	__in				size_t max_seg_size)
1152 {
1153 	tlv_cursor_t cursor;
1154 	struct tlv_partition_header *header;
1155 	struct tlv_partition_trailer *trailer;
1156 	size_t total_length;
1157 	uint32_t cksum;
1158 	int pos;
1159 	efx_rc_t rc;
1160 
1161 	EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
1162 
1163 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1164 		rc = EINVAL;
1165 		goto fail1;
1166 	}
1167 
1168 	/* Read initial chunk of the segment, starting at offset */
1169 	if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
1170 		    EF10_NVRAM_CHUNK,
1171 		    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
1172 		goto fail2;
1173 	}
1174 
1175 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1176 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1177 		    max_seg_size)) != 0) {
1178 		rc = EFAULT;
1179 		goto fail3;
1180 	}
1181 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1182 		rc = EINVAL;
1183 		goto fail4;
1184 	}
1185 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1186 
1187 	/* Check TLV segment length (includes the END tag) */
1188 	total_length = __LE_TO_CPU_32(header->total_length);
1189 	if (total_length > max_seg_size) {
1190 		rc = EFBIG;
1191 		goto fail5;
1192 	}
1193 
1194 	/* Read the remaining segment content */
1195 	if (total_length > EF10_NVRAM_CHUNK) {
1196 		if ((rc = ef10_nvram_partn_read_mode(enp, partn,
1197 			    seg_offset + EF10_NVRAM_CHUNK,
1198 			    seg_data + EF10_NVRAM_CHUNK,
1199 			    total_length - EF10_NVRAM_CHUNK,
1200 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
1201 			goto fail6;
1202 	}
1203 
1204 	/* Check segment ends with PARTITION_TRAILER and END tags */
1205 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1206 		rc = EINVAL;
1207 		goto fail7;
1208 	}
1209 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1210 
1211 	if ((rc = tlv_advance(&cursor)) != 0) {
1212 		rc = EINVAL;
1213 		goto fail8;
1214 	}
1215 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1216 		rc = EINVAL;
1217 		goto fail9;
1218 	}
1219 
1220 	/* Check data read from segment is consistent */
1221 	if (trailer->generation != header->generation) {
1222 		/*
1223 		 * The partition data may have been modified between successive
1224 		 * MCDI NVRAM_READ requests by the MC or another PCI function.
1225 		 *
1226 		 * The caller must retry to obtain consistent partition data.
1227 		 */
1228 		rc = EAGAIN;
1229 		goto fail10;
1230 	}
1231 
1232 	/* Verify segment checksum */
1233 	cksum = 0;
1234 	for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
1235 		cksum += *((uint32_t *)(seg_data + pos));
1236 	}
1237 	if (cksum != 0) {
1238 		rc = EINVAL;
1239 		goto fail11;
1240 	}
1241 
1242 	return (0);
1243 
1244 fail11:
1245 	EFSYS_PROBE(fail11);
1246 fail10:
1247 	EFSYS_PROBE(fail10);
1248 fail9:
1249 	EFSYS_PROBE(fail9);
1250 fail8:
1251 	EFSYS_PROBE(fail8);
1252 fail7:
1253 	EFSYS_PROBE(fail7);
1254 fail6:
1255 	EFSYS_PROBE(fail6);
1256 fail5:
1257 	EFSYS_PROBE(fail5);
1258 fail4:
1259 	EFSYS_PROBE(fail4);
1260 fail3:
1261 	EFSYS_PROBE(fail3);
1262 fail2:
1263 	EFSYS_PROBE(fail2);
1264 fail1:
1265 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1266 
1267 	return (rc);
1268 }
1269 
1270 /*
1271  * Read a single TLV item from a host memory
1272  * buffer containing a TLV formatted segment.
1273  */
1274 	__checkReturn		efx_rc_t
ef10_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)1275 ef10_nvram_buf_read_tlv(
1276 	__in				efx_nic_t *enp,
1277 	__in_bcount(max_seg_size)	caddr_t seg_data,
1278 	__in				size_t max_seg_size,
1279 	__in				uint32_t tag,
1280 	__deref_out_bcount_opt(*sizep)	caddr_t *datap,
1281 	__out				size_t *sizep)
1282 {
1283 	tlv_cursor_t cursor;
1284 	caddr_t data;
1285 	size_t length;
1286 	caddr_t value;
1287 	efx_rc_t rc;
1288 
1289 	_NOTE(ARGUNUSED(enp))
1290 
1291 	if ((seg_data == NULL) || (max_seg_size == 0)) {
1292 		rc = EINVAL;
1293 		goto fail1;
1294 	}
1295 
1296 	/* Find requested TLV tag in segment data */
1297 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1298 		    max_seg_size)) != 0) {
1299 		rc = EFAULT;
1300 		goto fail2;
1301 	}
1302 	if ((rc = tlv_find(&cursor, tag)) != 0) {
1303 		rc = ENOENT;
1304 		goto fail3;
1305 	}
1306 	value = (caddr_t)tlv_value(&cursor);
1307 	length = tlv_length(&cursor);
1308 
1309 	if (length == 0)
1310 		data = NULL;
1311 	else {
1312 		/* Copy out data from TLV item */
1313 		EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
1314 		if (data == NULL) {
1315 			rc = ENOMEM;
1316 			goto fail4;
1317 		}
1318 		memcpy(data, value, length);
1319 	}
1320 
1321 	*datap = data;
1322 	*sizep = length;
1323 
1324 	return (0);
1325 
1326 fail4:
1327 	EFSYS_PROBE(fail4);
1328 fail3:
1329 	EFSYS_PROBE(fail3);
1330 fail2:
1331 	EFSYS_PROBE(fail2);
1332 fail1:
1333 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1334 
1335 	return (rc);
1336 }
1337 
1338 /* Read a single TLV item from the first segment in a TLV formatted partition */
1339 	__checkReturn		efx_rc_t
ef10_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)1340 ef10_nvram_partn_read_tlv(
1341 	__in					efx_nic_t *enp,
1342 	__in					uint32_t partn,
1343 	__in					uint32_t tag,
1344 	__deref_out_bcount_opt(*seg_sizep)	caddr_t *seg_datap,
1345 	__out					size_t *seg_sizep)
1346 {
1347 	caddr_t seg_data = NULL;
1348 	size_t partn_size = 0;
1349 	size_t length;
1350 	caddr_t data;
1351 	int retry;
1352 	efx_rc_t rc;
1353 
1354 	/* Allocate sufficient memory for the entire partition */
1355 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1356 		goto fail1;
1357 
1358 	if (partn_size == 0) {
1359 		rc = ENOENT;
1360 		goto fail2;
1361 	}
1362 
1363 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
1364 	if (seg_data == NULL) {
1365 		rc = ENOMEM;
1366 		goto fail3;
1367 	}
1368 
1369 	/*
1370 	 * Read the first segment in a TLV partition. Retry until consistent
1371 	 * segment contents are returned. Inconsistent data may be read if:
1372 	 *  a) the segment contents are invalid
1373 	 *  b) the MC has rebooted while we were reading the partition
1374 	 *  c) the partition has been modified while we were reading it
1375 	 * Limit retry attempts to ensure forward progress.
1376 	 */
1377 	retry = 10;
1378 	do {
1379 		rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
1380 		    seg_data, partn_size);
1381 	} while ((rc == EAGAIN) && (--retry > 0));
1382 
1383 	if (rc != 0) {
1384 		/* Failed to obtain consistent segment data */
1385 		goto fail4;
1386 	}
1387 
1388 	if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
1389 		    tag, &data, &length)) != 0)
1390 		goto fail5;
1391 
1392 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1393 
1394 	*seg_datap = data;
1395 	*seg_sizep = length;
1396 
1397 	return (0);
1398 
1399 fail5:
1400 	EFSYS_PROBE(fail5);
1401 fail4:
1402 	EFSYS_PROBE(fail4);
1403 
1404 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
1405 fail3:
1406 	EFSYS_PROBE(fail3);
1407 fail2:
1408 	EFSYS_PROBE(fail2);
1409 fail1:
1410 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1411 
1412 	return (rc);
1413 }
1414 
1415 /* Compute the size of a segment. */
1416 	static	__checkReturn	efx_rc_t
ef10_nvram_buf_segment_size(__in caddr_t seg_data,__in size_t max_seg_size,__out size_t * seg_sizep)1417 ef10_nvram_buf_segment_size(
1418 	__in			caddr_t seg_data,
1419 	__in			size_t max_seg_size,
1420 	__out			size_t *seg_sizep)
1421 {
1422 	efx_rc_t rc;
1423 	tlv_cursor_t cursor;
1424 	struct tlv_partition_header *header;
1425 	uint32_t cksum;
1426 	int pos;
1427 	uint32_t *end_tag_position;
1428 	uint32_t segment_length;
1429 
1430 	/* A PARTITION_HEADER tag must be the first item at the given offset */
1431 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1432 		    max_seg_size)) != 0) {
1433 		rc = EFAULT;
1434 		goto fail1;
1435 	}
1436 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1437 		rc = EINVAL;
1438 		goto fail2;
1439 	}
1440 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1441 
1442 	/* Check TLV segment length (includes the END tag) */
1443 	*seg_sizep = __LE_TO_CPU_32(header->total_length);
1444 	if (*seg_sizep > max_seg_size) {
1445 		rc = EFBIG;
1446 		goto fail3;
1447 	}
1448 
1449 	/* Check segment ends with PARTITION_TRAILER and END tags */
1450 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1451 		rc = EINVAL;
1452 		goto fail4;
1453 	}
1454 
1455 	if ((rc = tlv_advance(&cursor)) != 0) {
1456 		rc = EINVAL;
1457 		goto fail5;
1458 	}
1459 	if (tlv_tag(&cursor) != TLV_TAG_END) {
1460 		rc = EINVAL;
1461 		goto fail6;
1462 	}
1463 	end_tag_position = cursor.current;
1464 
1465 	/* Verify segment checksum */
1466 	cksum = 0;
1467 	for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
1468 		cksum += *((uint32_t *)(seg_data + pos));
1469 	}
1470 	if (cksum != 0) {
1471 		rc = EINVAL;
1472 		goto fail7;
1473 	}
1474 
1475 	/*
1476 	 * Calculate total length from HEADER to END tags and compare to
1477 	 * max_seg_size and the total_length field in the HEADER tag.
1478 	 */
1479 	segment_length = tlv_block_length_used(&cursor);
1480 
1481 	if (segment_length > max_seg_size) {
1482 		rc = EINVAL;
1483 		goto fail8;
1484 	}
1485 
1486 	if (segment_length != *seg_sizep) {
1487 		rc = EINVAL;
1488 		goto fail9;
1489 	}
1490 
1491 	/* Skip over the first HEADER tag. */
1492 	rc = tlv_rewind(&cursor);
1493 	rc = tlv_advance(&cursor);
1494 
1495 	while (rc == 0) {
1496 		if (tlv_tag(&cursor) == TLV_TAG_END) {
1497 			/* Check that the END tag is the one found earlier. */
1498 			if (cursor.current != end_tag_position)
1499 				goto fail10;
1500 			break;
1501 		}
1502 		/* Check for duplicate HEADER tags before the END tag. */
1503 		if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
1504 			rc = EINVAL;
1505 			goto fail11;
1506 		}
1507 
1508 		rc = tlv_advance(&cursor);
1509 	}
1510 	if (rc != 0)
1511 		goto fail12;
1512 
1513 	return (0);
1514 
1515 fail12:
1516 	EFSYS_PROBE(fail12);
1517 fail11:
1518 	EFSYS_PROBE(fail11);
1519 fail10:
1520 	EFSYS_PROBE(fail10);
1521 fail9:
1522 	EFSYS_PROBE(fail9);
1523 fail8:
1524 	EFSYS_PROBE(fail8);
1525 fail7:
1526 	EFSYS_PROBE(fail7);
1527 fail6:
1528 	EFSYS_PROBE(fail6);
1529 fail5:
1530 	EFSYS_PROBE(fail5);
1531 fail4:
1532 	EFSYS_PROBE(fail4);
1533 fail3:
1534 	EFSYS_PROBE(fail3);
1535 fail2:
1536 	EFSYS_PROBE(fail2);
1537 fail1:
1538 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1539 
1540 	return (rc);
1541 }
1542 
1543 /*
1544  * Add or update a single TLV item in a host memory buffer containing a TLV
1545  * formatted segment. Historically partitions consisted of only one segment.
1546  */
1547 	__checkReturn			efx_rc_t
ef10_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)1548 ef10_nvram_buf_write_tlv(
1549 	__inout_bcount(max_seg_size)	caddr_t seg_data,
1550 	__in				size_t max_seg_size,
1551 	__in				uint32_t tag,
1552 	__in_bcount(tag_size)		caddr_t tag_data,
1553 	__in				size_t tag_size,
1554 	__out				size_t *total_lengthp)
1555 {
1556 	tlv_cursor_t cursor;
1557 	struct tlv_partition_header *header;
1558 	struct tlv_partition_trailer *trailer;
1559 	uint32_t generation;
1560 	uint32_t cksum;
1561 	int pos;
1562 	efx_rc_t rc;
1563 
1564 	/* A PARTITION_HEADER tag must be the first item (at offset zero) */
1565 	if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
1566 			max_seg_size)) != 0) {
1567 		rc = EFAULT;
1568 		goto fail1;
1569 	}
1570 	if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
1571 		rc = EINVAL;
1572 		goto fail2;
1573 	}
1574 	header = (struct tlv_partition_header *)tlv_item(&cursor);
1575 
1576 	/* Update the TLV chain to contain the new data */
1577 	if ((rc = tlv_find(&cursor, tag)) == 0) {
1578 		/* Modify existing TLV item */
1579 		if ((rc = tlv_modify(&cursor, tag,
1580 			    (uint8_t *)tag_data, tag_size)) != 0)
1581 			goto fail3;
1582 	} else {
1583 		/* Insert a new TLV item before the PARTITION_TRAILER */
1584 		rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
1585 		if (rc != 0) {
1586 			rc = EINVAL;
1587 			goto fail4;
1588 		}
1589 		if ((rc = tlv_insert(&cursor, tag,
1590 			    (uint8_t *)tag_data, tag_size)) != 0) {
1591 			rc = EINVAL;
1592 			goto fail5;
1593 		}
1594 	}
1595 
1596 	/* Find the trailer tag */
1597 	if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
1598 		rc = EINVAL;
1599 		goto fail6;
1600 	}
1601 	trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
1602 
1603 	/* Update PARTITION_HEADER and PARTITION_TRAILER fields */
1604 	*total_lengthp = tlv_block_length_used(&cursor);
1605 	if (*total_lengthp > max_seg_size) {
1606 		rc = ENOSPC;
1607 		goto fail7;
1608 	}
1609 	generation = __LE_TO_CPU_32(header->generation) + 1;
1610 
1611 	header->total_length	= __CPU_TO_LE_32(*total_lengthp);
1612 	header->generation	= __CPU_TO_LE_32(generation);
1613 	trailer->generation	= __CPU_TO_LE_32(generation);
1614 
1615 	/* Recompute PARTITION_TRAILER checksum */
1616 	trailer->checksum = 0;
1617 	cksum = 0;
1618 	for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
1619 		cksum += *((uint32_t *)(seg_data + pos));
1620 	}
1621 	trailer->checksum = ~cksum + 1;
1622 
1623 	return (0);
1624 
1625 fail7:
1626 	EFSYS_PROBE(fail7);
1627 fail6:
1628 	EFSYS_PROBE(fail6);
1629 fail5:
1630 	EFSYS_PROBE(fail5);
1631 fail4:
1632 	EFSYS_PROBE(fail4);
1633 fail3:
1634 	EFSYS_PROBE(fail3);
1635 fail2:
1636 	EFSYS_PROBE(fail2);
1637 fail1:
1638 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1639 
1640 	return (rc);
1641 }
1642 
1643 /*
1644  * Add or update a single TLV item in the first segment of a TLV formatted
1645  * dynamic config partition. The first segment is the current active
1646  * configuration.
1647  */
1648 	__checkReturn		efx_rc_t
ef10_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)1649 ef10_nvram_partn_write_tlv(
1650 	__in			efx_nic_t *enp,
1651 	__in			uint32_t partn,
1652 	__in			uint32_t tag,
1653 	__in_bcount(size)	caddr_t data,
1654 	__in			size_t size)
1655 {
1656 	return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
1657 	    size, B_FALSE);
1658 }
1659 
1660 /*
1661  * Read a segment from nvram at the given offset into a buffer (segment_data)
1662  * and optionally write a new tag to it.
1663  */
1664 static	__checkReturn		efx_rc_t
ef10_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)1665 ef10_nvram_segment_write_tlv(
1666 	__in			efx_nic_t *enp,
1667 	__in			uint32_t partn,
1668 	__in			uint32_t tag,
1669 	__in_bcount(size)	caddr_t data,
1670 	__in			size_t size,
1671 	__inout			caddr_t *seg_datap,
1672 	__inout			size_t *partn_offsetp,
1673 	__inout			size_t *src_remain_lenp,
1674 	__inout			size_t *dest_remain_lenp,
1675 	__in			boolean_t write)
1676 {
1677 	efx_rc_t rc;
1678 	efx_rc_t status;
1679 	size_t original_segment_size;
1680 	size_t modified_segment_size;
1681 
1682 	/*
1683 	 * Read the segment from NVRAM into the segment_data buffer and validate
1684 	 * it, returning if it does not validate. This is not a failure unless
1685 	 * this is the first segment in a partition. In this case the caller
1686 	 * must propagate the error.
1687 	 */
1688 	status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
1689 	    *seg_datap, *src_remain_lenp);
1690 	if (status != 0) {
1691 		rc = EINVAL;
1692 		goto fail1;
1693 	}
1694 
1695 	status = ef10_nvram_buf_segment_size(*seg_datap,
1696 	    *src_remain_lenp, &original_segment_size);
1697 	if (status != 0) {
1698 		rc = EINVAL;
1699 		goto fail2;
1700 	}
1701 
1702 	if (write) {
1703 		/* Update the contents of the segment in the buffer */
1704 		if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
1705 			*dest_remain_lenp, tag, data, size,
1706 			&modified_segment_size)) != 0) {
1707 			goto fail3;
1708 		}
1709 		*dest_remain_lenp -= modified_segment_size;
1710 		*seg_datap += modified_segment_size;
1711 	} else {
1712 		/*
1713 		 * We won't modify this segment, but still need to update the
1714 		 * remaining lengths and pointers.
1715 		 */
1716 		*dest_remain_lenp -= original_segment_size;
1717 		*seg_datap += original_segment_size;
1718 	}
1719 
1720 	*partn_offsetp += original_segment_size;
1721 	*src_remain_lenp -= original_segment_size;
1722 
1723 	return (0);
1724 
1725 fail3:
1726 	EFSYS_PROBE(fail3);
1727 fail2:
1728 	EFSYS_PROBE(fail2);
1729 fail1:
1730 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1731 
1732 	return (rc);
1733 }
1734 
1735 /*
1736  * Add or update a single TLV item in either the first segment or in all
1737  * segments in a TLV formatted dynamic config partition. Dynamic config
1738  * partitions on boards that support RFID are divided into a number of segments,
1739  * each formatted like a partition, with header, trailer and end tags. The first
1740  * segment is the current active configuration.
1741  *
1742  * The segments are initialised by manftest and each contain a different
1743  * configuration e.g. firmware variant. The firmware can be instructed
1744  * via RFID to copy a segment to replace the first segment, hence changing the
1745  * active configuration.  This allows ops to change the configuration of a board
1746  * prior to shipment using RFID.
1747  *
1748  * Changes to the dynamic config may need to be written to all segments (e.g.
1749  * firmware versions) or just the first segment (changes to the active
1750  * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
1751  * If only the first segment is written the code still needs to be aware of the
1752  * possible presence of subsequent segments as writing to a segment may cause
1753  * its size to increase, which would overwrite the subsequent segments and
1754  * invalidate them.
1755  */
1756 	__checkReturn		efx_rc_t
ef10_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)1757 ef10_nvram_partn_write_segment_tlv(
1758 	__in			efx_nic_t *enp,
1759 	__in			uint32_t partn,
1760 	__in			uint32_t tag,
1761 	__in_bcount(size)	caddr_t data,
1762 	__in			size_t size,
1763 	__in			boolean_t all_segments)
1764 {
1765 	size_t partn_size = 0;
1766 	caddr_t partn_data;
1767 	size_t total_length = 0;
1768 	efx_rc_t rc;
1769 	size_t current_offset = 0;
1770 	size_t remaining_original_length;
1771 	size_t remaining_modified_length;
1772 	caddr_t segment_data;
1773 
1774 	EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
1775 
1776 	/* Allocate sufficient memory for the entire partition */
1777 	if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
1778 		goto fail1;
1779 
1780 	EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
1781 	if (partn_data == NULL) {
1782 		rc = ENOMEM;
1783 		goto fail2;
1784 	}
1785 
1786 	remaining_original_length = partn_size;
1787 	remaining_modified_length = partn_size;
1788 	segment_data = partn_data;
1789 
1790 	/* Lock the partition */
1791 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
1792 		goto fail3;
1793 
1794 	/* Iterate over each (potential) segment to update it. */
1795 	do {
1796 		boolean_t write = all_segments || current_offset == 0;
1797 
1798 		rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
1799 		    &segment_data, &current_offset, &remaining_original_length,
1800 		    &remaining_modified_length, write);
1801 		if (rc != 0) {
1802 			if (current_offset == 0) {
1803 				/*
1804 				 * If no data has been read then the first
1805 				 * segment is invalid, which is an error.
1806 				 */
1807 				goto fail4;
1808 			}
1809 			break;
1810 		}
1811 	} while (current_offset < partn_size);
1812 
1813 	total_length = segment_data - partn_data;
1814 
1815 	/*
1816 	 * We've run out of space.  This should actually be dealt with by
1817 	 * ef10_nvram_buf_write_tlv returning ENOSPC.
1818 	 */
1819 	if (total_length > partn_size) {
1820 		rc = ENOSPC;
1821 		goto fail5;
1822 	}
1823 
1824 	/* Erase the whole partition in NVRAM */
1825 	if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
1826 		goto fail6;
1827 
1828 	/* Write new partition contents from the buffer to NVRAM */
1829 	if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
1830 		    total_length)) != 0)
1831 		goto fail7;
1832 
1833 	/* Unlock the partition */
1834 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1835 
1836 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1837 
1838 	return (0);
1839 
1840 fail7:
1841 	EFSYS_PROBE(fail7);
1842 fail6:
1843 	EFSYS_PROBE(fail6);
1844 fail5:
1845 	EFSYS_PROBE(fail5);
1846 fail4:
1847 	EFSYS_PROBE(fail4);
1848 
1849 	(void) ef10_nvram_partn_unlock(enp, partn, NULL);
1850 fail3:
1851 	EFSYS_PROBE(fail3);
1852 
1853 	EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
1854 fail2:
1855 	EFSYS_PROBE(fail2);
1856 fail1:
1857 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1858 
1859 	return (rc);
1860 }
1861 
1862 /*
1863  * Get the size of a NVRAM partition. This is the total size allocated in nvram,
1864  * not the data used by the segments in the partition.
1865  */
1866 	__checkReturn		efx_rc_t
ef10_nvram_partn_size(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * sizep)1867 ef10_nvram_partn_size(
1868 	__in			efx_nic_t *enp,
1869 	__in			uint32_t partn,
1870 	__out			size_t *sizep)
1871 {
1872 	efx_rc_t rc;
1873 
1874 	if ((rc = efx_mcdi_nvram_info(enp, partn, sizep,
1875 	    NULL, NULL, NULL)) != 0)
1876 		goto fail1;
1877 
1878 	return (0);
1879 
1880 fail1:
1881 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1882 
1883 	return (rc);
1884 }
1885 
1886 	__checkReturn		efx_rc_t
ef10_nvram_partn_lock(__in efx_nic_t * enp,__in uint32_t partn)1887 ef10_nvram_partn_lock(
1888 	__in			efx_nic_t *enp,
1889 	__in			uint32_t partn)
1890 {
1891 	efx_rc_t rc;
1892 
1893 	if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
1894 		goto fail1;
1895 
1896 	return (0);
1897 
1898 fail1:
1899 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1900 
1901 	return (rc);
1902 }
1903 
1904 	__checkReturn		efx_rc_t
ef10_nvram_partn_read_mode(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size,__in uint32_t mode)1905 ef10_nvram_partn_read_mode(
1906 	__in			efx_nic_t *enp,
1907 	__in			uint32_t partn,
1908 	__in			unsigned int offset,
1909 	__out_bcount(size)	caddr_t data,
1910 	__in			size_t size,
1911 	__in			uint32_t mode)
1912 {
1913 	size_t chunk;
1914 	efx_rc_t rc;
1915 
1916 	while (size > 0) {
1917 		chunk = MIN(size, EF10_NVRAM_CHUNK);
1918 
1919 		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
1920 			    data, chunk, mode)) != 0) {
1921 			goto fail1;
1922 		}
1923 
1924 		size -= chunk;
1925 		data += chunk;
1926 		offset += chunk;
1927 	}
1928 
1929 	return (0);
1930 
1931 fail1:
1932 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1933 
1934 	return (rc);
1935 }
1936 
1937 	__checkReturn		efx_rc_t
ef10_nvram_partn_read(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)1938 ef10_nvram_partn_read(
1939 	__in			efx_nic_t *enp,
1940 	__in			uint32_t partn,
1941 	__in			unsigned int offset,
1942 	__out_bcount(size)	caddr_t data,
1943 	__in			size_t size)
1944 {
1945 	/*
1946 	 * Read requests which come in through the EFX API expect to
1947 	 * read the current, active partition.
1948 	 */
1949 	return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
1950 			    MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
1951 }
1952 
1953 	__checkReturn		efx_rc_t
ef10_nvram_partn_erase(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__in size_t size)1954 ef10_nvram_partn_erase(
1955 	__in			efx_nic_t *enp,
1956 	__in			uint32_t partn,
1957 	__in			unsigned int offset,
1958 	__in			size_t size)
1959 {
1960 	efx_rc_t rc;
1961 	uint32_t erase_size;
1962 
1963 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
1964 	    &erase_size, NULL)) != 0)
1965 		goto fail1;
1966 
1967 	if (erase_size == 0) {
1968 		if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
1969 			goto fail2;
1970 	} else {
1971 		if (size % erase_size != 0) {
1972 			rc = EINVAL;
1973 			goto fail3;
1974 		}
1975 		while (size > 0) {
1976 			if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
1977 			    erase_size)) != 0)
1978 				goto fail4;
1979 			offset += erase_size;
1980 			size -= erase_size;
1981 		}
1982 	}
1983 
1984 	return (0);
1985 
1986 fail4:
1987 	EFSYS_PROBE(fail4);
1988 fail3:
1989 	EFSYS_PROBE(fail3);
1990 fail2:
1991 	EFSYS_PROBE(fail2);
1992 fail1:
1993 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
1994 
1995 	return (rc);
1996 }
1997 
1998 	__checkReturn		efx_rc_t
ef10_nvram_partn_write(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__in_bcount (size)caddr_t data,__in size_t size)1999 ef10_nvram_partn_write(
2000 	__in			efx_nic_t *enp,
2001 	__in			uint32_t partn,
2002 	__in			unsigned int offset,
2003 	__in_bcount(size)	caddr_t data,
2004 	__in			size_t size)
2005 {
2006 	size_t chunk;
2007 	uint32_t write_size;
2008 	efx_rc_t rc;
2009 
2010 	if ((rc = efx_mcdi_nvram_info(enp, partn, NULL, NULL,
2011 	    NULL, &write_size)) != 0)
2012 		goto fail1;
2013 
2014 	if (write_size != 0) {
2015 		/*
2016 		 * Check that the size is a multiple of the write chunk size if
2017 		 * the write chunk size is available.
2018 		 */
2019 		if (size % write_size != 0) {
2020 			rc = EINVAL;
2021 			goto fail2;
2022 		}
2023 	} else {
2024 		write_size = EF10_NVRAM_CHUNK;
2025 	}
2026 
2027 	while (size > 0) {
2028 		chunk = MIN(size, write_size);
2029 
2030 		if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
2031 			    data, chunk)) != 0) {
2032 			goto fail3;
2033 		}
2034 
2035 		size -= chunk;
2036 		data += chunk;
2037 		offset += chunk;
2038 	}
2039 
2040 	return (0);
2041 
2042 fail3:
2043 	EFSYS_PROBE(fail3);
2044 fail2:
2045 	EFSYS_PROBE(fail2);
2046 fail1:
2047 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2048 
2049 	return (rc);
2050 }
2051 
2052 	__checkReturn		efx_rc_t
ef10_nvram_partn_unlock(__in efx_nic_t * enp,__in uint32_t partn,__out_opt uint32_t * resultp)2053 ef10_nvram_partn_unlock(
2054 	__in			efx_nic_t *enp,
2055 	__in			uint32_t partn,
2056 	__out_opt		uint32_t *resultp)
2057 {
2058 	boolean_t reboot = B_FALSE;
2059 	efx_rc_t rc;
2060 
2061 	if (resultp != NULL)
2062 		*resultp = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
2063 
2064 	rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, resultp);
2065 	if (rc != 0)
2066 		goto fail1;
2067 
2068 	return (0);
2069 
2070 fail1:
2071 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2072 
2073 	return (rc);
2074 }
2075 
2076 	__checkReturn		efx_rc_t
2077 ef10_nvram_partn_set_version(
2078 	__in			efx_nic_t *enp,
2079 	__in			uint32_t partn,
2080 	__in_ecount(4)		uint16_t version[4])
2081 {
2082 	struct tlv_partition_version partn_version;
2083 	size_t size;
2084 	efx_rc_t rc;
2085 
2086 	/* Add or modify partition version TLV item */
2087 	partn_version.version_w = __CPU_TO_LE_16(version[0]);
2088 	partn_version.version_x = __CPU_TO_LE_16(version[1]);
2089 	partn_version.version_y = __CPU_TO_LE_16(version[2]);
2090 	partn_version.version_z = __CPU_TO_LE_16(version[3]);
2091 
2092 	size = sizeof (partn_version) - (2 * sizeof (uint32_t));
2093 
2094 	/* Write the version number to all segments in the partition */
2095 	if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
2096 		    NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
2097 		    TLV_TAG_PARTITION_VERSION(partn),
2098 		    (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
2099 		goto fail1;
2100 
2101 	return (0);
2102 
2103 fail1:
2104 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2105 
2106 	return (rc);
2107 }
2108 
2109 #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
2110 
2111 #if EFSYS_OPT_NVRAM
2112 
2113 typedef struct ef10_parttbl_entry_s {
2114 	unsigned int		partn;
2115 	unsigned int		port;
2116 	efx_nvram_type_t	nvtype;
2117 } ef10_parttbl_entry_t;
2118 
2119 /* Translate EFX NVRAM types to firmware partition types */
2120 static ef10_parttbl_entry_t hunt_parttbl[] = {
2121 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2122 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2123 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2124 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2125 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2126 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2127 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2128 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2129 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2130 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2131 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2132 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2133 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2134 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG},
2135 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT2, 3, EFX_NVRAM_BOOTROM_CFG},
2136 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT3, 4, EFX_NVRAM_BOOTROM_CFG},
2137 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2138 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2139 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2140 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2141 	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2142 	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2143 	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2144 	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2145 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2146 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2147 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2148 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2149 	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2150 	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2151 	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2152 	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE}
2153 };
2154 
2155 static ef10_parttbl_entry_t medford_parttbl[] = {
2156 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   1, EFX_NVRAM_MC_FIRMWARE},
2157 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   2, EFX_NVRAM_MC_FIRMWARE},
2158 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   3, EFX_NVRAM_MC_FIRMWARE},
2159 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE,	   4, EFX_NVRAM_MC_FIRMWARE},
2160 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  1, EFX_NVRAM_MC_GOLDEN},
2161 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  2, EFX_NVRAM_MC_GOLDEN},
2162 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  3, EFX_NVRAM_MC_GOLDEN},
2163 	{NVRAM_PARTITION_TYPE_MC_FIRMWARE_BACKUP,  4, EFX_NVRAM_MC_GOLDEN},
2164 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   1, EFX_NVRAM_BOOTROM},
2165 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   2, EFX_NVRAM_BOOTROM},
2166 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   3, EFX_NVRAM_BOOTROM},
2167 	{NVRAM_PARTITION_TYPE_EXPANSION_ROM,	   4, EFX_NVRAM_BOOTROM},
2168 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG},
2169 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 2, EFX_NVRAM_BOOTROM_CFG},
2170 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 3, EFX_NVRAM_BOOTROM_CFG},
2171 	{NVRAM_PARTITION_TYPE_EXPROM_CONFIG_PORT0, 4, EFX_NVRAM_BOOTROM_CFG},
2172 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   1, EFX_NVRAM_DYNAMIC_CFG},
2173 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   2, EFX_NVRAM_DYNAMIC_CFG},
2174 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   3, EFX_NVRAM_DYNAMIC_CFG},
2175 	{NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,	   4, EFX_NVRAM_DYNAMIC_CFG},
2176 	{NVRAM_PARTITION_TYPE_FPGA,		   1, EFX_NVRAM_FPGA},
2177 	{NVRAM_PARTITION_TYPE_FPGA,		   2, EFX_NVRAM_FPGA},
2178 	{NVRAM_PARTITION_TYPE_FPGA,		   3, EFX_NVRAM_FPGA},
2179 	{NVRAM_PARTITION_TYPE_FPGA,		   4, EFX_NVRAM_FPGA},
2180 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   1, EFX_NVRAM_FPGA_BACKUP},
2181 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   2, EFX_NVRAM_FPGA_BACKUP},
2182 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   3, EFX_NVRAM_FPGA_BACKUP},
2183 	{NVRAM_PARTITION_TYPE_FPGA_BACKUP,	   4, EFX_NVRAM_FPGA_BACKUP},
2184 	{NVRAM_PARTITION_TYPE_LICENSE,		   1, EFX_NVRAM_LICENSE},
2185 	{NVRAM_PARTITION_TYPE_LICENSE,		   2, EFX_NVRAM_LICENSE},
2186 	{NVRAM_PARTITION_TYPE_LICENSE,		   3, EFX_NVRAM_LICENSE},
2187 	{NVRAM_PARTITION_TYPE_LICENSE,		   4, EFX_NVRAM_LICENSE},
2188 	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   1, EFX_NVRAM_UEFIROM},
2189 	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   2, EFX_NVRAM_UEFIROM},
2190 	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   3, EFX_NVRAM_UEFIROM},
2191 	{NVRAM_PARTITION_TYPE_EXPANSION_UEFI,	   4, EFX_NVRAM_UEFIROM}
2192 };
2193 
2194 static	__checkReturn		efx_rc_t
ef10_parttbl_get(__in efx_nic_t * enp,__out ef10_parttbl_entry_t ** parttblp,__out size_t * parttbl_rowsp)2195 ef10_parttbl_get(
2196 	__in			efx_nic_t *enp,
2197 	__out			ef10_parttbl_entry_t **parttblp,
2198 	__out			size_t *parttbl_rowsp)
2199 {
2200 	switch (enp->en_family) {
2201 	case EFX_FAMILY_HUNTINGTON:
2202 		*parttblp = hunt_parttbl;
2203 		*parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
2204 		break;
2205 
2206 	case EFX_FAMILY_MEDFORD:
2207 		*parttblp = medford_parttbl;
2208 		*parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
2209 		break;
2210 
2211 	default:
2212 		EFSYS_ASSERT(B_FALSE);
2213 		return (EINVAL);
2214 	}
2215 	return (0);
2216 }
2217 
2218 	__checkReturn		efx_rc_t
ef10_nvram_type_to_partn(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out uint32_t * partnp)2219 ef10_nvram_type_to_partn(
2220 	__in			efx_nic_t *enp,
2221 	__in			efx_nvram_type_t type,
2222 	__out			uint32_t *partnp)
2223 {
2224 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2225 	ef10_parttbl_entry_t *parttbl = NULL;
2226 	size_t parttbl_rows = 0;
2227 	unsigned int i;
2228 
2229 	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
2230 	EFSYS_ASSERT(partnp != NULL);
2231 
2232 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2233 		for (i = 0; i < parttbl_rows; i++) {
2234 			ef10_parttbl_entry_t *entry = &parttbl[i];
2235 
2236 			if (entry->nvtype == type &&
2237 			    entry->port == emip->emi_port) {
2238 				*partnp = entry->partn;
2239 				return (0);
2240 			}
2241 		}
2242 	}
2243 
2244 	return (ENOTSUP);
2245 }
2246 
2247 #if EFSYS_OPT_DIAG
2248 
2249 static	__checkReturn		efx_rc_t
ef10_nvram_partn_to_type(__in efx_nic_t * enp,__in uint32_t partn,__out efx_nvram_type_t * typep)2250 ef10_nvram_partn_to_type(
2251 	__in			efx_nic_t *enp,
2252 	__in			uint32_t partn,
2253 	__out			efx_nvram_type_t *typep)
2254 {
2255 	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
2256 	ef10_parttbl_entry_t *parttbl = NULL;
2257 	size_t parttbl_rows = 0;
2258 	unsigned int i;
2259 
2260 	EFSYS_ASSERT(typep != NULL);
2261 
2262 	if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
2263 		for (i = 0; i < parttbl_rows; i++) {
2264 			ef10_parttbl_entry_t *entry = &parttbl[i];
2265 
2266 			if (entry->partn == partn &&
2267 			    entry->port == emip->emi_port) {
2268 				*typep = entry->nvtype;
2269 				return (0);
2270 			}
2271 		}
2272 	}
2273 
2274 	return (ENOTSUP);
2275 }
2276 
2277 	__checkReturn		efx_rc_t
ef10_nvram_test(__in efx_nic_t * enp)2278 ef10_nvram_test(
2279 	__in			efx_nic_t *enp)
2280 {
2281 	efx_nvram_type_t type;
2282 	unsigned int npartns = 0;
2283 	uint32_t *partns = NULL;
2284 	size_t size;
2285 	unsigned int i;
2286 	efx_rc_t rc;
2287 
2288 	/* Read available partitions from NVRAM partition map */
2289 	size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
2290 	EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
2291 	if (partns == NULL) {
2292 		rc = ENOMEM;
2293 		goto fail1;
2294 	}
2295 
2296 	if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
2297 		    &npartns)) != 0) {
2298 		goto fail2;
2299 	}
2300 
2301 	for (i = 0; i < npartns; i++) {
2302 		/* Check if the partition is supported for this port */
2303 		if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
2304 			continue;
2305 
2306 		if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
2307 			goto fail3;
2308 	}
2309 
2310 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2311 	return (0);
2312 
2313 fail3:
2314 	EFSYS_PROBE(fail3);
2315 fail2:
2316 	EFSYS_PROBE(fail2);
2317 	EFSYS_KMEM_FREE(enp->en_esip, size, partns);
2318 fail1:
2319 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2320 	return (rc);
2321 }
2322 
2323 #endif	/* EFSYS_OPT_DIAG */
2324 
2325 	__checkReturn		efx_rc_t
2326 ef10_nvram_partn_get_version(
2327 	__in			efx_nic_t *enp,
2328 	__in			uint32_t partn,
2329 	__out			uint32_t *subtypep,
2330 	__out_ecount(4)		uint16_t version[4])
2331 {
2332 	efx_rc_t rc;
2333 
2334 	/* FIXME: get highest partn version from all ports */
2335 	/* FIXME: return partn description if available */
2336 
2337 	if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
2338 		    version, NULL, 0)) != 0)
2339 		goto fail1;
2340 
2341 	return (0);
2342 
2343 fail1:
2344 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2345 
2346 	return (rc);
2347 }
2348 
2349 	__checkReturn		efx_rc_t
ef10_nvram_partn_rw_start(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * chunk_sizep)2350 ef10_nvram_partn_rw_start(
2351 	__in			efx_nic_t *enp,
2352 	__in			uint32_t partn,
2353 	__out			size_t *chunk_sizep)
2354 {
2355 	efx_rc_t rc;
2356 
2357 	if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
2358 		goto fail1;
2359 
2360 	if (chunk_sizep != NULL)
2361 		*chunk_sizep = EF10_NVRAM_CHUNK;
2362 
2363 	return (0);
2364 
2365 fail1:
2366 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2367 
2368 	return (rc);
2369 }
2370 
2371 	__checkReturn		efx_rc_t
ef10_nvram_partn_rw_finish(__in efx_nic_t * enp,__in uint32_t partn)2372 ef10_nvram_partn_rw_finish(
2373 	__in			efx_nic_t *enp,
2374 	__in			uint32_t partn)
2375 {
2376 	efx_rc_t rc;
2377 
2378 	if ((rc = ef10_nvram_partn_unlock(enp, partn, NULL)) != 0)
2379 		goto fail1;
2380 
2381 	return (0);
2382 
2383 fail1:
2384 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
2385 
2386 	return (rc);
2387 }
2388 
2389 #endif	/* EFSYS_OPT_NVRAM */
2390 
2391 #endif	/* EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD */
2392