xref: /freebsd/sys/dev/sfxge/common/ef10_image.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
1391763d7SAndrew Rybchenko /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3391763d7SAndrew Rybchenko  *
4391763d7SAndrew Rybchenko  * Copyright (c) 2017-2018 Solarflare Communications Inc.
5391763d7SAndrew Rybchenko  * All rights reserved.
6391763d7SAndrew Rybchenko  *
7391763d7SAndrew Rybchenko  * Redistribution and use in source and binary forms, with or without
8391763d7SAndrew Rybchenko  * modification, are permitted provided that the following conditions are met:
9391763d7SAndrew Rybchenko  *
10391763d7SAndrew Rybchenko  * 1. Redistributions of source code must retain the above copyright notice,
11391763d7SAndrew Rybchenko  *    this list of conditions and the following disclaimer.
12391763d7SAndrew Rybchenko  * 2. Redistributions in binary form must reproduce the above copyright notice,
13391763d7SAndrew Rybchenko  *    this list of conditions and the following disclaimer in the documentation
14391763d7SAndrew Rybchenko  *    and/or other materials provided with the distribution.
15391763d7SAndrew Rybchenko  *
16391763d7SAndrew Rybchenko  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17391763d7SAndrew Rybchenko  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18391763d7SAndrew Rybchenko  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19391763d7SAndrew Rybchenko  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20391763d7SAndrew Rybchenko  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21391763d7SAndrew Rybchenko  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22391763d7SAndrew Rybchenko  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23391763d7SAndrew Rybchenko  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24391763d7SAndrew Rybchenko  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25391763d7SAndrew Rybchenko  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26391763d7SAndrew Rybchenko  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27391763d7SAndrew Rybchenko  *
28391763d7SAndrew Rybchenko  * The views and conclusions contained in the software and documentation are
29391763d7SAndrew Rybchenko  * those of the authors and should not be interpreted as representing official
30391763d7SAndrew Rybchenko  * policies, either expressed or implied, of the FreeBSD Project.
31391763d7SAndrew Rybchenko  */
32391763d7SAndrew Rybchenko 
33391763d7SAndrew Rybchenko #include <sys/cdefs.h>
34391763d7SAndrew Rybchenko #include "efx.h"
35391763d7SAndrew Rybchenko #include "efx_impl.h"
36391763d7SAndrew Rybchenko 
37391763d7SAndrew Rybchenko #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
38391763d7SAndrew Rybchenko 
39391763d7SAndrew Rybchenko #if EFSYS_OPT_IMAGE_LAYOUT
40391763d7SAndrew Rybchenko 
4102d7c0c3SAndrew Rybchenko /*
4202d7c0c3SAndrew Rybchenko  * Utility routines to support limited parsing of ASN.1 tags. This is not a
4302d7c0c3SAndrew Rybchenko  * general purpose ASN.1 parser, but is sufficient to locate the required
4402d7c0c3SAndrew Rybchenko  * objects in a signed image with CMS headers.
4502d7c0c3SAndrew Rybchenko  */
4602d7c0c3SAndrew Rybchenko 
4702d7c0c3SAndrew Rybchenko /* DER encodings for ASN.1 tags (see ITU-T X.690) */
4802d7c0c3SAndrew Rybchenko #define	ASN1_TAG_INTEGER	    (0x02)
4902d7c0c3SAndrew Rybchenko #define	ASN1_TAG_OCTET_STRING	    (0x04)
5002d7c0c3SAndrew Rybchenko #define	ASN1_TAG_OBJ_ID		    (0x06)
5102d7c0c3SAndrew Rybchenko #define	ASN1_TAG_SEQUENCE	    (0x30)
5202d7c0c3SAndrew Rybchenko #define	ASN1_TAG_SET		    (0x31)
5302d7c0c3SAndrew Rybchenko 
5402d7c0c3SAndrew Rybchenko #define	ASN1_TAG_IS_PRIM(tag)	    ((tag & 0x20) == 0)
5502d7c0c3SAndrew Rybchenko 
5602d7c0c3SAndrew Rybchenko #define	ASN1_TAG_PRIM_CONTEXT(n)    (0x80 + (n))
5702d7c0c3SAndrew Rybchenko #define	ASN1_TAG_CONS_CONTEXT(n)    (0xA0 + (n))
5802d7c0c3SAndrew Rybchenko 
5902d7c0c3SAndrew Rybchenko typedef struct efx_asn1_cursor_s {
6002d7c0c3SAndrew Rybchenko 	uint8_t		*buffer;
6102d7c0c3SAndrew Rybchenko 	uint32_t	length;
6202d7c0c3SAndrew Rybchenko 
6302d7c0c3SAndrew Rybchenko 	uint8_t		tag;
6402d7c0c3SAndrew Rybchenko 	uint32_t	hdr_size;
6502d7c0c3SAndrew Rybchenko 	uint32_t	val_size;
6602d7c0c3SAndrew Rybchenko } efx_asn1_cursor_t;
6702d7c0c3SAndrew Rybchenko 
6802d7c0c3SAndrew Rybchenko /* Parse header of DER encoded ASN.1 TLV and match tag */
6902d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_asn1_parse_header_match_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)7002d7c0c3SAndrew Rybchenko efx_asn1_parse_header_match_tag(
7102d7c0c3SAndrew Rybchenko 	__inout		efx_asn1_cursor_t	*cursor,
7202d7c0c3SAndrew Rybchenko 	__in		uint8_t			tag)
7302d7c0c3SAndrew Rybchenko {
7402d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
7502d7c0c3SAndrew Rybchenko 
7602d7c0c3SAndrew Rybchenko 	if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) {
7702d7c0c3SAndrew Rybchenko 		rc = EINVAL;
7802d7c0c3SAndrew Rybchenko 		goto fail1;
7902d7c0c3SAndrew Rybchenko 	}
8002d7c0c3SAndrew Rybchenko 
8102d7c0c3SAndrew Rybchenko 	cursor->tag = cursor->buffer[0];
8202d7c0c3SAndrew Rybchenko 	if (cursor->tag != tag) {
8302d7c0c3SAndrew Rybchenko 		/* Tag not matched */
8402d7c0c3SAndrew Rybchenko 		rc = ENOENT;
8502d7c0c3SAndrew Rybchenko 		goto fail2;
8602d7c0c3SAndrew Rybchenko 	}
8702d7c0c3SAndrew Rybchenko 
8802d7c0c3SAndrew Rybchenko 	if ((cursor->tag & 0x1F) == 0x1F) {
8902d7c0c3SAndrew Rybchenko 		/* Long tag format not used in CMS syntax */
9002d7c0c3SAndrew Rybchenko 		rc = EINVAL;
9102d7c0c3SAndrew Rybchenko 		goto fail3;
9202d7c0c3SAndrew Rybchenko 	}
9302d7c0c3SAndrew Rybchenko 
9402d7c0c3SAndrew Rybchenko 	if ((cursor->buffer[1] & 0x80) == 0) {
9502d7c0c3SAndrew Rybchenko 		/* Short form: length is 0..127 */
9602d7c0c3SAndrew Rybchenko 		cursor->hdr_size = 2;
9702d7c0c3SAndrew Rybchenko 		cursor->val_size = cursor->buffer[1];
9802d7c0c3SAndrew Rybchenko 	} else {
9902d7c0c3SAndrew Rybchenko 		/* Long form: length encoded as [0x80+nbytes][length bytes] */
10002d7c0c3SAndrew Rybchenko 		uint32_t nbytes = cursor->buffer[1] & 0x7F;
10102d7c0c3SAndrew Rybchenko 		uint32_t offset;
10202d7c0c3SAndrew Rybchenko 
10302d7c0c3SAndrew Rybchenko 		if (nbytes == 0) {
10402d7c0c3SAndrew Rybchenko 			/* Indefinite length not allowed in DER encoding */
10502d7c0c3SAndrew Rybchenko 			rc = EINVAL;
10602d7c0c3SAndrew Rybchenko 			goto fail4;
10702d7c0c3SAndrew Rybchenko 		}
10802d7c0c3SAndrew Rybchenko 		if (2 + nbytes > cursor->length) {
10902d7c0c3SAndrew Rybchenko 			/* Header length overflows image buffer */
11002d7c0c3SAndrew Rybchenko 			rc = EINVAL;
11102d7c0c3SAndrew Rybchenko 			goto fail6;
11202d7c0c3SAndrew Rybchenko 		}
11302d7c0c3SAndrew Rybchenko 		if (nbytes > sizeof (uint32_t)) {
11402d7c0c3SAndrew Rybchenko 			/* Length encoding too big */
11502d7c0c3SAndrew Rybchenko 			rc = E2BIG;
11602d7c0c3SAndrew Rybchenko 			goto fail5;
11702d7c0c3SAndrew Rybchenko 		}
11802d7c0c3SAndrew Rybchenko 		cursor->hdr_size = 2 + nbytes;
11902d7c0c3SAndrew Rybchenko 		cursor->val_size = 0;
12002d7c0c3SAndrew Rybchenko 		for (offset = 2; offset < cursor->hdr_size; offset++) {
12102d7c0c3SAndrew Rybchenko 			cursor->val_size =
12202d7c0c3SAndrew Rybchenko 			    (cursor->val_size << 8) | cursor->buffer[offset];
12302d7c0c3SAndrew Rybchenko 		}
12402d7c0c3SAndrew Rybchenko 	}
12502d7c0c3SAndrew Rybchenko 
12602d7c0c3SAndrew Rybchenko 	if ((cursor->hdr_size + cursor->val_size) > cursor->length) {
12702d7c0c3SAndrew Rybchenko 		/* Length overflows image buffer */
12802d7c0c3SAndrew Rybchenko 		rc = E2BIG;
12902d7c0c3SAndrew Rybchenko 		goto fail7;
13002d7c0c3SAndrew Rybchenko 	}
13102d7c0c3SAndrew Rybchenko 
13202d7c0c3SAndrew Rybchenko 	return (0);
13302d7c0c3SAndrew Rybchenko 
13402d7c0c3SAndrew Rybchenko fail7:
13502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail7);
13602d7c0c3SAndrew Rybchenko fail6:
13702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail6);
13802d7c0c3SAndrew Rybchenko fail5:
13902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail5);
14002d7c0c3SAndrew Rybchenko fail4:
14102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
14202d7c0c3SAndrew Rybchenko fail3:
14302d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
14402d7c0c3SAndrew Rybchenko fail2:
14502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
14602d7c0c3SAndrew Rybchenko fail1:
14702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
14802d7c0c3SAndrew Rybchenko 
14902d7c0c3SAndrew Rybchenko 	return (rc);
15002d7c0c3SAndrew Rybchenko }
15102d7c0c3SAndrew Rybchenko 
15202d7c0c3SAndrew Rybchenko /* Enter nested ASN.1 TLV (contained in value of current TLV) */
15302d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_asn1_enter_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)15402d7c0c3SAndrew Rybchenko efx_asn1_enter_tag(
15502d7c0c3SAndrew Rybchenko 	__inout		efx_asn1_cursor_t	*cursor,
15602d7c0c3SAndrew Rybchenko 	__in		uint8_t			tag)
15702d7c0c3SAndrew Rybchenko {
15802d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
15902d7c0c3SAndrew Rybchenko 
16002d7c0c3SAndrew Rybchenko 	if (cursor == NULL) {
16102d7c0c3SAndrew Rybchenko 		rc = EINVAL;
16202d7c0c3SAndrew Rybchenko 		goto fail1;
16302d7c0c3SAndrew Rybchenko 	}
16402d7c0c3SAndrew Rybchenko 
16502d7c0c3SAndrew Rybchenko 	if (ASN1_TAG_IS_PRIM(tag)) {
16602d7c0c3SAndrew Rybchenko 		/* Cannot enter a primitive tag */
16702d7c0c3SAndrew Rybchenko 		rc = ENOTSUP;
16802d7c0c3SAndrew Rybchenko 		goto fail2;
16902d7c0c3SAndrew Rybchenko 	}
17002d7c0c3SAndrew Rybchenko 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
17102d7c0c3SAndrew Rybchenko 	if (rc != 0) {
17202d7c0c3SAndrew Rybchenko 		/* Invalid TLV or wrong tag */
17302d7c0c3SAndrew Rybchenko 		goto fail3;
17402d7c0c3SAndrew Rybchenko 	}
17502d7c0c3SAndrew Rybchenko 
17602d7c0c3SAndrew Rybchenko 	/* Limit cursor range to nested TLV */
17702d7c0c3SAndrew Rybchenko 	cursor->buffer += cursor->hdr_size;
17802d7c0c3SAndrew Rybchenko 	cursor->length = cursor->val_size;
17902d7c0c3SAndrew Rybchenko 
18002d7c0c3SAndrew Rybchenko 	return (0);
18102d7c0c3SAndrew Rybchenko 
18202d7c0c3SAndrew Rybchenko fail3:
18302d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
18402d7c0c3SAndrew Rybchenko fail2:
18502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
18602d7c0c3SAndrew Rybchenko fail1:
18702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
18802d7c0c3SAndrew Rybchenko 
18902d7c0c3SAndrew Rybchenko 	return (rc);
19002d7c0c3SAndrew Rybchenko }
19102d7c0c3SAndrew Rybchenko 
19202d7c0c3SAndrew Rybchenko /*
19302d7c0c3SAndrew Rybchenko  * Check that the current ASN.1 TLV matches the given tag and value.
19402d7c0c3SAndrew Rybchenko  * Advance cursor to next TLV on a successful match.
19502d7c0c3SAndrew Rybchenko  */
19602d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_asn1_match_tag_value(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag,__in const void * valp,__in uint32_t val_size)19702d7c0c3SAndrew Rybchenko efx_asn1_match_tag_value(
19802d7c0c3SAndrew Rybchenko 	__inout		efx_asn1_cursor_t	*cursor,
19902d7c0c3SAndrew Rybchenko 	__in		uint8_t			tag,
20002d7c0c3SAndrew Rybchenko 	__in		const void		*valp,
20102d7c0c3SAndrew Rybchenko 	__in		uint32_t		val_size)
20202d7c0c3SAndrew Rybchenko {
20302d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
20402d7c0c3SAndrew Rybchenko 
20502d7c0c3SAndrew Rybchenko 	if (cursor == NULL) {
20602d7c0c3SAndrew Rybchenko 		rc = EINVAL;
20702d7c0c3SAndrew Rybchenko 		goto fail1;
20802d7c0c3SAndrew Rybchenko 	}
20902d7c0c3SAndrew Rybchenko 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
21002d7c0c3SAndrew Rybchenko 	if (rc != 0) {
21102d7c0c3SAndrew Rybchenko 		/* Invalid TLV or wrong tag */
21202d7c0c3SAndrew Rybchenko 		goto fail2;
21302d7c0c3SAndrew Rybchenko 	}
21402d7c0c3SAndrew Rybchenko 	if (cursor->val_size != val_size) {
21502d7c0c3SAndrew Rybchenko 		/* Value size is different */
21602d7c0c3SAndrew Rybchenko 		rc = EINVAL;
21702d7c0c3SAndrew Rybchenko 		goto fail3;
21802d7c0c3SAndrew Rybchenko 	}
21902d7c0c3SAndrew Rybchenko 	if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) {
22002d7c0c3SAndrew Rybchenko 		/* Value content is different */
22102d7c0c3SAndrew Rybchenko 		rc = EINVAL;
22202d7c0c3SAndrew Rybchenko 		goto fail4;
22302d7c0c3SAndrew Rybchenko 	}
22402d7c0c3SAndrew Rybchenko 	cursor->buffer += cursor->hdr_size + cursor->val_size;
22502d7c0c3SAndrew Rybchenko 	cursor->length -= cursor->hdr_size + cursor->val_size;
22602d7c0c3SAndrew Rybchenko 
22702d7c0c3SAndrew Rybchenko 	return (0);
22802d7c0c3SAndrew Rybchenko 
22902d7c0c3SAndrew Rybchenko fail4:
23002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
23102d7c0c3SAndrew Rybchenko fail3:
23202d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
23302d7c0c3SAndrew Rybchenko fail2:
23402d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
23502d7c0c3SAndrew Rybchenko fail1:
23602d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
23702d7c0c3SAndrew Rybchenko 
23802d7c0c3SAndrew Rybchenko 	return (rc);
23902d7c0c3SAndrew Rybchenko }
24002d7c0c3SAndrew Rybchenko 
24102d7c0c3SAndrew Rybchenko /* Advance cursor to next TLV */
24202d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_asn1_skip_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)24302d7c0c3SAndrew Rybchenko efx_asn1_skip_tag(
24402d7c0c3SAndrew Rybchenko 	__inout		efx_asn1_cursor_t	*cursor,
24502d7c0c3SAndrew Rybchenko 	__in		uint8_t			tag)
24602d7c0c3SAndrew Rybchenko {
24702d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
24802d7c0c3SAndrew Rybchenko 
24902d7c0c3SAndrew Rybchenko 	if (cursor == NULL) {
25002d7c0c3SAndrew Rybchenko 		rc = EINVAL;
25102d7c0c3SAndrew Rybchenko 		goto fail1;
25202d7c0c3SAndrew Rybchenko 	}
25302d7c0c3SAndrew Rybchenko 
25402d7c0c3SAndrew Rybchenko 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
25502d7c0c3SAndrew Rybchenko 	if (rc != 0) {
25602d7c0c3SAndrew Rybchenko 		/* Invalid TLV or wrong tag */
25702d7c0c3SAndrew Rybchenko 		goto fail2;
25802d7c0c3SAndrew Rybchenko 	}
25902d7c0c3SAndrew Rybchenko 	cursor->buffer += cursor->hdr_size + cursor->val_size;
26002d7c0c3SAndrew Rybchenko 	cursor->length -= cursor->hdr_size + cursor->val_size;
26102d7c0c3SAndrew Rybchenko 
26202d7c0c3SAndrew Rybchenko 	return (0);
26302d7c0c3SAndrew Rybchenko 
26402d7c0c3SAndrew Rybchenko fail2:
26502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
26602d7c0c3SAndrew Rybchenko fail1:
26702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
26802d7c0c3SAndrew Rybchenko 
26902d7c0c3SAndrew Rybchenko 	return (rc);
27002d7c0c3SAndrew Rybchenko }
27102d7c0c3SAndrew Rybchenko 
27202d7c0c3SAndrew Rybchenko /* Return pointer to value octets and value size from current TLV */
27302d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_asn1_get_tag_value(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag,__out uint8_t ** valp,__out uint32_t * val_sizep)27402d7c0c3SAndrew Rybchenko efx_asn1_get_tag_value(
27502d7c0c3SAndrew Rybchenko 	__inout		efx_asn1_cursor_t	*cursor,
27602d7c0c3SAndrew Rybchenko 	__in		uint8_t			tag,
27702d7c0c3SAndrew Rybchenko 	__out		uint8_t			**valp,
27802d7c0c3SAndrew Rybchenko 	__out		uint32_t		*val_sizep)
27902d7c0c3SAndrew Rybchenko {
28002d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
28102d7c0c3SAndrew Rybchenko 
28202d7c0c3SAndrew Rybchenko 	if (cursor == NULL || valp == NULL || val_sizep == NULL) {
28302d7c0c3SAndrew Rybchenko 		rc = EINVAL;
28402d7c0c3SAndrew Rybchenko 		goto fail1;
28502d7c0c3SAndrew Rybchenko 	}
28602d7c0c3SAndrew Rybchenko 
28702d7c0c3SAndrew Rybchenko 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
28802d7c0c3SAndrew Rybchenko 	if (rc != 0) {
28902d7c0c3SAndrew Rybchenko 		/* Invalid TLV or wrong tag */
29002d7c0c3SAndrew Rybchenko 		goto fail2;
29102d7c0c3SAndrew Rybchenko 	}
29202d7c0c3SAndrew Rybchenko 	*valp = cursor->buffer + cursor->hdr_size;
29302d7c0c3SAndrew Rybchenko 	*val_sizep = cursor->val_size;
29402d7c0c3SAndrew Rybchenko 
29502d7c0c3SAndrew Rybchenko 	return (0);
29602d7c0c3SAndrew Rybchenko 
29702d7c0c3SAndrew Rybchenko fail2:
29802d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
29902d7c0c3SAndrew Rybchenko fail1:
30002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
30102d7c0c3SAndrew Rybchenko 
30202d7c0c3SAndrew Rybchenko 	return (rc);
30302d7c0c3SAndrew Rybchenko }
30402d7c0c3SAndrew Rybchenko 
30502d7c0c3SAndrew Rybchenko /*
30602d7c0c3SAndrew Rybchenko  * Utility routines for parsing CMS headers (see RFC2315, PKCS#7)
30702d7c0c3SAndrew Rybchenko  */
30802d7c0c3SAndrew Rybchenko 
30902d7c0c3SAndrew Rybchenko /* OID 1.2.840.113549.1.7.2 */
31002d7c0c3SAndrew Rybchenko static const uint8_t PKCS7_SignedData[] =
31102d7c0c3SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
31202d7c0c3SAndrew Rybchenko 
31302d7c0c3SAndrew Rybchenko /* OID 1.2.840.113549.1.7.1 */
31402d7c0c3SAndrew Rybchenko static const uint8_t PKCS7_Data[] =
31502d7c0c3SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
31602d7c0c3SAndrew Rybchenko 
31702d7c0c3SAndrew Rybchenko /* SignedData structure version */
31802d7c0c3SAndrew Rybchenko static const uint8_t SignedData_Version[] =
31902d7c0c3SAndrew Rybchenko { 0x03 };
32002d7c0c3SAndrew Rybchenko 
32102d7c0c3SAndrew Rybchenko /*
32202d7c0c3SAndrew Rybchenko  * Check for a valid image in signed image format. This uses CMS syntax
32302d7c0c3SAndrew Rybchenko  * (see RFC2315, PKCS#7) to provide signatures, and certificates required
32402d7c0c3SAndrew Rybchenko  * to validate the signatures. The encapsulated content is in unsigned image
32502d7c0c3SAndrew Rybchenko  * format (reflash header, image code, trailer checksum).
32602d7c0c3SAndrew Rybchenko  */
32702d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_check_signed_image_header(__in void * bufferp,__in uint32_t buffer_size,__out uint32_t * content_offsetp,__out uint32_t * content_lengthp)32802d7c0c3SAndrew Rybchenko efx_check_signed_image_header(
32902d7c0c3SAndrew Rybchenko 	__in		void		*bufferp,
33002d7c0c3SAndrew Rybchenko 	__in		uint32_t	buffer_size,
33102d7c0c3SAndrew Rybchenko 	__out		uint32_t	*content_offsetp,
33202d7c0c3SAndrew Rybchenko 	__out		uint32_t	*content_lengthp)
33302d7c0c3SAndrew Rybchenko {
33402d7c0c3SAndrew Rybchenko 	efx_asn1_cursor_t cursor;
33502d7c0c3SAndrew Rybchenko 	uint8_t *valp;
33602d7c0c3SAndrew Rybchenko 	uint32_t val_size;
33702d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
33802d7c0c3SAndrew Rybchenko 
33902d7c0c3SAndrew Rybchenko 	if (content_offsetp == NULL || content_lengthp == NULL) {
34002d7c0c3SAndrew Rybchenko 		rc = EINVAL;
34102d7c0c3SAndrew Rybchenko 		goto fail1;
34202d7c0c3SAndrew Rybchenko 	}
34302d7c0c3SAndrew Rybchenko 	cursor.buffer = (uint8_t *)bufferp;
34402d7c0c3SAndrew Rybchenko 	cursor.length = buffer_size;
34502d7c0c3SAndrew Rybchenko 
34602d7c0c3SAndrew Rybchenko 	/* ContextInfo */
34702d7c0c3SAndrew Rybchenko 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
34802d7c0c3SAndrew Rybchenko 	if (rc != 0)
34902d7c0c3SAndrew Rybchenko 		goto fail2;
35002d7c0c3SAndrew Rybchenko 
35102d7c0c3SAndrew Rybchenko 	/* ContextInfo.contentType */
35202d7c0c3SAndrew Rybchenko 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
35302d7c0c3SAndrew Rybchenko 	    PKCS7_SignedData, sizeof (PKCS7_SignedData));
35402d7c0c3SAndrew Rybchenko 	if (rc != 0)
35502d7c0c3SAndrew Rybchenko 		goto fail3;
35602d7c0c3SAndrew Rybchenko 
35702d7c0c3SAndrew Rybchenko 	/* ContextInfo.content */
35802d7c0c3SAndrew Rybchenko 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
35902d7c0c3SAndrew Rybchenko 	if (rc != 0)
36002d7c0c3SAndrew Rybchenko 		goto fail4;
36102d7c0c3SAndrew Rybchenko 
36202d7c0c3SAndrew Rybchenko 	/* SignedData */
36302d7c0c3SAndrew Rybchenko 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
36402d7c0c3SAndrew Rybchenko 	if (rc != 0)
36502d7c0c3SAndrew Rybchenko 		goto fail5;
36602d7c0c3SAndrew Rybchenko 
36702d7c0c3SAndrew Rybchenko 	/* SignedData.version */
36802d7c0c3SAndrew Rybchenko 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER,
36902d7c0c3SAndrew Rybchenko 	    SignedData_Version, sizeof (SignedData_Version));
37002d7c0c3SAndrew Rybchenko 	if (rc != 0)
37102d7c0c3SAndrew Rybchenko 		goto fail6;
37202d7c0c3SAndrew Rybchenko 
37302d7c0c3SAndrew Rybchenko 	/* SignedData.digestAlgorithms */
37402d7c0c3SAndrew Rybchenko 	rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET);
37502d7c0c3SAndrew Rybchenko 	if (rc != 0)
37602d7c0c3SAndrew Rybchenko 		goto fail7;
37702d7c0c3SAndrew Rybchenko 
37802d7c0c3SAndrew Rybchenko 	/* SignedData.encapContentInfo */
37902d7c0c3SAndrew Rybchenko 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
38002d7c0c3SAndrew Rybchenko 	if (rc != 0)
38102d7c0c3SAndrew Rybchenko 		goto fail8;
38202d7c0c3SAndrew Rybchenko 
38302d7c0c3SAndrew Rybchenko 	/* SignedData.encapContentInfo.econtentType */
38402d7c0c3SAndrew Rybchenko 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
38502d7c0c3SAndrew Rybchenko 	    PKCS7_Data, sizeof (PKCS7_Data));
38602d7c0c3SAndrew Rybchenko 	if (rc != 0)
38702d7c0c3SAndrew Rybchenko 		goto fail9;
38802d7c0c3SAndrew Rybchenko 
38902d7c0c3SAndrew Rybchenko 	/* SignedData.encapContentInfo.econtent */
39002d7c0c3SAndrew Rybchenko 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
39102d7c0c3SAndrew Rybchenko 	if (rc != 0)
39202d7c0c3SAndrew Rybchenko 		goto fail10;
39302d7c0c3SAndrew Rybchenko 
39402d7c0c3SAndrew Rybchenko 	/*
39502d7c0c3SAndrew Rybchenko 	 * The octet string contains the image header, image code bytes and
39602d7c0c3SAndrew Rybchenko 	 * image trailer CRC (same as unsigned image layout).
39702d7c0c3SAndrew Rybchenko 	 */
39802d7c0c3SAndrew Rybchenko 	valp = NULL;
39902d7c0c3SAndrew Rybchenko 	val_size = 0;
40002d7c0c3SAndrew Rybchenko 	rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING,
40102d7c0c3SAndrew Rybchenko 	    &valp, &val_size);
40202d7c0c3SAndrew Rybchenko 	if (rc != 0)
40302d7c0c3SAndrew Rybchenko 		goto fail11;
40402d7c0c3SAndrew Rybchenko 
40502d7c0c3SAndrew Rybchenko 	if ((valp == NULL) || (val_size == 0)) {
40602d7c0c3SAndrew Rybchenko 		rc = EINVAL;
40702d7c0c3SAndrew Rybchenko 		goto fail12;
40802d7c0c3SAndrew Rybchenko 	}
40902d7c0c3SAndrew Rybchenko 	if (valp < (uint8_t *)bufferp) {
41002d7c0c3SAndrew Rybchenko 		rc = EINVAL;
41102d7c0c3SAndrew Rybchenko 		goto fail13;
41202d7c0c3SAndrew Rybchenko 	}
41302d7c0c3SAndrew Rybchenko 	if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) {
41402d7c0c3SAndrew Rybchenko 		rc = EINVAL;
41502d7c0c3SAndrew Rybchenko 		goto fail14;
41602d7c0c3SAndrew Rybchenko 	}
41702d7c0c3SAndrew Rybchenko 
41802d7c0c3SAndrew Rybchenko 	*content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp);
41902d7c0c3SAndrew Rybchenko 	*content_lengthp = val_size;
42002d7c0c3SAndrew Rybchenko 
42102d7c0c3SAndrew Rybchenko 	return (0);
42202d7c0c3SAndrew Rybchenko 
42302d7c0c3SAndrew Rybchenko fail14:
42402d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail14);
42502d7c0c3SAndrew Rybchenko fail13:
42602d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail13);
42702d7c0c3SAndrew Rybchenko fail12:
42802d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail12);
42902d7c0c3SAndrew Rybchenko fail11:
43002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail11);
43102d7c0c3SAndrew Rybchenko fail10:
43202d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail10);
43302d7c0c3SAndrew Rybchenko fail9:
43402d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail9);
43502d7c0c3SAndrew Rybchenko fail8:
43602d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail8);
43702d7c0c3SAndrew Rybchenko fail7:
43802d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail7);
43902d7c0c3SAndrew Rybchenko fail6:
44002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail6);
44102d7c0c3SAndrew Rybchenko fail5:
44202d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail5);
44302d7c0c3SAndrew Rybchenko fail4:
44402d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
44502d7c0c3SAndrew Rybchenko fail3:
44602d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
44702d7c0c3SAndrew Rybchenko fail2:
44802d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
44902d7c0c3SAndrew Rybchenko fail1:
45002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
45102d7c0c3SAndrew Rybchenko 
45202d7c0c3SAndrew Rybchenko 	return (rc);
45302d7c0c3SAndrew Rybchenko }
45402d7c0c3SAndrew Rybchenko 
45502d7c0c3SAndrew Rybchenko static	__checkReturn	efx_rc_t
efx_check_unsigned_image(__in void * bufferp,__in uint32_t buffer_size)45602d7c0c3SAndrew Rybchenko efx_check_unsigned_image(
45702d7c0c3SAndrew Rybchenko 	__in		void		*bufferp,
45802d7c0c3SAndrew Rybchenko 	__in		uint32_t	buffer_size)
45902d7c0c3SAndrew Rybchenko {
46002d7c0c3SAndrew Rybchenko 	efx_image_header_t *header;
46102d7c0c3SAndrew Rybchenko 	efx_image_trailer_t *trailer;
46202d7c0c3SAndrew Rybchenko 	uint32_t crc;
46302d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
46402d7c0c3SAndrew Rybchenko 
46502d7c0c3SAndrew Rybchenko 	EFX_STATIC_ASSERT(sizeof (*header) == EFX_IMAGE_HEADER_SIZE);
46602d7c0c3SAndrew Rybchenko 	EFX_STATIC_ASSERT(sizeof (*trailer) == EFX_IMAGE_TRAILER_SIZE);
46702d7c0c3SAndrew Rybchenko 
46802d7c0c3SAndrew Rybchenko 	/* Must have at least enough space for required image header fields */
46902d7c0c3SAndrew Rybchenko 	if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) +
47002d7c0c3SAndrew Rybchenko 		sizeof (header->eih_size))) {
47102d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
47202d7c0c3SAndrew Rybchenko 		goto fail1;
47302d7c0c3SAndrew Rybchenko 	}
47402d7c0c3SAndrew Rybchenko 	header = (efx_image_header_t *)bufferp;
47502d7c0c3SAndrew Rybchenko 
47602d7c0c3SAndrew Rybchenko 	if (header->eih_magic != EFX_IMAGE_HEADER_MAGIC) {
47702d7c0c3SAndrew Rybchenko 		rc = EINVAL;
47802d7c0c3SAndrew Rybchenko 		goto fail2;
47902d7c0c3SAndrew Rybchenko 	}
48002d7c0c3SAndrew Rybchenko 
48102d7c0c3SAndrew Rybchenko 	/*
48202d7c0c3SAndrew Rybchenko 	 * Check image header version is same or higher than lowest required
48302d7c0c3SAndrew Rybchenko 	 * version.
48402d7c0c3SAndrew Rybchenko 	 */
48502d7c0c3SAndrew Rybchenko 	if (header->eih_version < EFX_IMAGE_HEADER_VERSION) {
48602d7c0c3SAndrew Rybchenko 		rc = EINVAL;
48702d7c0c3SAndrew Rybchenko 		goto fail3;
48802d7c0c3SAndrew Rybchenko 	}
48902d7c0c3SAndrew Rybchenko 
49002d7c0c3SAndrew Rybchenko 	/* Buffer must have space for image header, code and image trailer. */
49102d7c0c3SAndrew Rybchenko 	if (buffer_size < (header->eih_size + header->eih_code_size +
49202d7c0c3SAndrew Rybchenko 		EFX_IMAGE_TRAILER_SIZE)) {
49302d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
49402d7c0c3SAndrew Rybchenko 		goto fail4;
49502d7c0c3SAndrew Rybchenko 	}
49602d7c0c3SAndrew Rybchenko 
49702d7c0c3SAndrew Rybchenko 	/* Check CRC from image buffer matches computed CRC. */
49802d7c0c3SAndrew Rybchenko 	trailer = (efx_image_trailer_t *)((uint8_t *)header +
49902d7c0c3SAndrew Rybchenko 	    header->eih_size + header->eih_code_size);
50002d7c0c3SAndrew Rybchenko 
50102d7c0c3SAndrew Rybchenko 	crc = efx_crc32_calculate(0, (uint8_t *)header,
50202d7c0c3SAndrew Rybchenko 	    (header->eih_size + header->eih_code_size));
50302d7c0c3SAndrew Rybchenko 
50402d7c0c3SAndrew Rybchenko 	if (trailer->eit_crc != crc) {
50502d7c0c3SAndrew Rybchenko 		rc = EINVAL;
50602d7c0c3SAndrew Rybchenko 		goto fail5;
50702d7c0c3SAndrew Rybchenko 	}
50802d7c0c3SAndrew Rybchenko 
50902d7c0c3SAndrew Rybchenko 	return (0);
51002d7c0c3SAndrew Rybchenko 
51102d7c0c3SAndrew Rybchenko fail5:
51202d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail5);
51302d7c0c3SAndrew Rybchenko fail4:
51402d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
51502d7c0c3SAndrew Rybchenko fail3:
51602d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
51702d7c0c3SAndrew Rybchenko fail2:
51802d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
51902d7c0c3SAndrew Rybchenko fail1:
52002d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
52102d7c0c3SAndrew Rybchenko 
52202d7c0c3SAndrew Rybchenko 	return (rc);
52302d7c0c3SAndrew Rybchenko }
52402d7c0c3SAndrew Rybchenko 
52502d7c0c3SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_check_reflash_image(__in void * bufferp,__in uint32_t buffer_size,__out efx_image_info_t * infop)52602d7c0c3SAndrew Rybchenko efx_check_reflash_image(
52702d7c0c3SAndrew Rybchenko 	__in		void			*bufferp,
52802d7c0c3SAndrew Rybchenko 	__in		uint32_t		buffer_size,
52902d7c0c3SAndrew Rybchenko 	__out		efx_image_info_t	*infop)
53002d7c0c3SAndrew Rybchenko {
53102d7c0c3SAndrew Rybchenko 	efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE;
53202d7c0c3SAndrew Rybchenko 	uint32_t image_offset;
53302d7c0c3SAndrew Rybchenko 	uint32_t image_size;
53402d7c0c3SAndrew Rybchenko 	void *imagep;
53502d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
53602d7c0c3SAndrew Rybchenko 
53702d7c0c3SAndrew Rybchenko 	EFSYS_ASSERT(infop != NULL);
53802d7c0c3SAndrew Rybchenko 	if (infop == NULL) {
53902d7c0c3SAndrew Rybchenko 		rc = EINVAL;
54002d7c0c3SAndrew Rybchenko 		goto fail1;
54102d7c0c3SAndrew Rybchenko 	}
54202d7c0c3SAndrew Rybchenko 	memset(infop, 0, sizeof (*infop));
54302d7c0c3SAndrew Rybchenko 
54402d7c0c3SAndrew Rybchenko 	if (bufferp == NULL || buffer_size == 0) {
54502d7c0c3SAndrew Rybchenko 		rc = EINVAL;
54602d7c0c3SAndrew Rybchenko 		goto fail2;
54702d7c0c3SAndrew Rybchenko 	}
54802d7c0c3SAndrew Rybchenko 
54902d7c0c3SAndrew Rybchenko 	/*
55002d7c0c3SAndrew Rybchenko 	 * Check if the buffer contains an image in signed format, and if so,
55102d7c0c3SAndrew Rybchenko 	 * locate the image header.
55202d7c0c3SAndrew Rybchenko 	 */
55302d7c0c3SAndrew Rybchenko 	rc = efx_check_signed_image_header(bufferp, buffer_size,
55402d7c0c3SAndrew Rybchenko 	    &image_offset, &image_size);
55502d7c0c3SAndrew Rybchenko 	if (rc == 0) {
55602d7c0c3SAndrew Rybchenko 		/*
55702d7c0c3SAndrew Rybchenko 		 * Buffer holds signed image format. Check that the encapsulated
55802d7c0c3SAndrew Rybchenko 		 * content is in unsigned image format.
55902d7c0c3SAndrew Rybchenko 		 */
56002d7c0c3SAndrew Rybchenko 		format = EFX_IMAGE_FORMAT_SIGNED;
56102d7c0c3SAndrew Rybchenko 	} else {
56202d7c0c3SAndrew Rybchenko 		/* Check if the buffer holds image in unsigned image format */
56302d7c0c3SAndrew Rybchenko 		format = EFX_IMAGE_FORMAT_UNSIGNED;
56402d7c0c3SAndrew Rybchenko 		image_offset = 0;
56502d7c0c3SAndrew Rybchenko 		image_size = buffer_size;
56602d7c0c3SAndrew Rybchenko 	}
56702d7c0c3SAndrew Rybchenko 	if (image_offset + image_size > buffer_size) {
56802d7c0c3SAndrew Rybchenko 		rc = E2BIG;
56902d7c0c3SAndrew Rybchenko 		goto fail3;
57002d7c0c3SAndrew Rybchenko 	}
57102d7c0c3SAndrew Rybchenko 	imagep = (uint8_t *)bufferp + image_offset;
57202d7c0c3SAndrew Rybchenko 
57302d7c0c3SAndrew Rybchenko 	/* Check unsigned image layout (image header, code, image trailer) */
57402d7c0c3SAndrew Rybchenko 	rc = efx_check_unsigned_image(imagep, image_size);
57502d7c0c3SAndrew Rybchenko 	if (rc != 0)
57602d7c0c3SAndrew Rybchenko 		goto fail4;
57702d7c0c3SAndrew Rybchenko 
57802d7c0c3SAndrew Rybchenko 	/* Return image details */
57902d7c0c3SAndrew Rybchenko 	infop->eii_format = format;
58002d7c0c3SAndrew Rybchenko 	infop->eii_imagep = bufferp;
58102d7c0c3SAndrew Rybchenko 	infop->eii_image_size = buffer_size;
58202d7c0c3SAndrew Rybchenko 	infop->eii_headerp = (efx_image_header_t *)imagep;
58302d7c0c3SAndrew Rybchenko 
58402d7c0c3SAndrew Rybchenko 	return (0);
58502d7c0c3SAndrew Rybchenko 
58602d7c0c3SAndrew Rybchenko fail4:
58702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
58802d7c0c3SAndrew Rybchenko fail3:
58902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
59002d7c0c3SAndrew Rybchenko fail2:
59102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
59202d7c0c3SAndrew Rybchenko 	infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
59302d7c0c3SAndrew Rybchenko 	infop->eii_imagep = NULL;
59402d7c0c3SAndrew Rybchenko 	infop->eii_image_size = 0;
59502d7c0c3SAndrew Rybchenko 
59602d7c0c3SAndrew Rybchenko fail1:
59702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
59802d7c0c3SAndrew Rybchenko 
59902d7c0c3SAndrew Rybchenko 	return (rc);
60002d7c0c3SAndrew Rybchenko }
60102d7c0c3SAndrew Rybchenko 
60202d7c0c3SAndrew Rybchenko 	__checkReturn	efx_rc_t
efx_build_signed_image_write_buffer(__out_bcount (buffer_size)uint8_t * bufferp,__in uint32_t buffer_size,__in efx_image_info_t * infop,__out efx_image_header_t ** headerpp)60302d7c0c3SAndrew Rybchenko efx_build_signed_image_write_buffer(
60402d7c0c3SAndrew Rybchenko 	__out_bcount(buffer_size)
60502d7c0c3SAndrew Rybchenko 			uint8_t			*bufferp,
60602d7c0c3SAndrew Rybchenko 	__in		uint32_t		buffer_size,
60702d7c0c3SAndrew Rybchenko 	__in		efx_image_info_t	*infop,
60802d7c0c3SAndrew Rybchenko 	__out		efx_image_header_t	**headerpp)
60902d7c0c3SAndrew Rybchenko {
61002d7c0c3SAndrew Rybchenko 	signed_image_chunk_hdr_t chunk_hdr;
61102d7c0c3SAndrew Rybchenko 	uint32_t hdr_offset;
61202d7c0c3SAndrew Rybchenko 	struct {
61302d7c0c3SAndrew Rybchenko 		uint32_t offset;
61402d7c0c3SAndrew Rybchenko 		uint32_t size;
61502d7c0c3SAndrew Rybchenko 	} cms_header, image_header, code, image_trailer, signature;
61602d7c0c3SAndrew Rybchenko 	efx_rc_t rc;
61702d7c0c3SAndrew Rybchenko 
61802d7c0c3SAndrew Rybchenko 	EFSYS_ASSERT((infop != NULL) && (headerpp != NULL));
61902d7c0c3SAndrew Rybchenko 
62002d7c0c3SAndrew Rybchenko 	if ((bufferp == NULL) || (buffer_size == 0) ||
62102d7c0c3SAndrew Rybchenko 	    (infop == NULL) || (headerpp == NULL)) {
62202d7c0c3SAndrew Rybchenko 		/* Invalid arguments */
62302d7c0c3SAndrew Rybchenko 		rc = EINVAL;
62402d7c0c3SAndrew Rybchenko 		goto fail1;
62502d7c0c3SAndrew Rybchenko 	}
62602d7c0c3SAndrew Rybchenko 	if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) ||
62702d7c0c3SAndrew Rybchenko 	    (infop->eii_imagep == NULL) ||
62802d7c0c3SAndrew Rybchenko 	    (infop->eii_headerp == NULL) ||
62902d7c0c3SAndrew Rybchenko 	    ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) ||
63002d7c0c3SAndrew Rybchenko 	    (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) ||
63102d7c0c3SAndrew Rybchenko 	    ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) >
63202d7c0c3SAndrew Rybchenko 	    (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) {
63302d7c0c3SAndrew Rybchenko 		/* Invalid image info */
63402d7c0c3SAndrew Rybchenko 		rc = EINVAL;
63502d7c0c3SAndrew Rybchenko 		goto fail2;
63602d7c0c3SAndrew Rybchenko 	}
63702d7c0c3SAndrew Rybchenko 
63802d7c0c3SAndrew Rybchenko 	/* Locate image chunks in original signed image */
63902d7c0c3SAndrew Rybchenko 	cms_header.offset = 0;
64002d7c0c3SAndrew Rybchenko 	cms_header.size =
64102d7c0c3SAndrew Rybchenko 	    (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep);
64202d7c0c3SAndrew Rybchenko 	if ((cms_header.size > buffer_size) ||
64302d7c0c3SAndrew Rybchenko 	    (cms_header.offset > (buffer_size - cms_header.size))) {
64402d7c0c3SAndrew Rybchenko 		rc = EINVAL;
64502d7c0c3SAndrew Rybchenko 		goto fail3;
64602d7c0c3SAndrew Rybchenko 	}
64702d7c0c3SAndrew Rybchenko 
64802d7c0c3SAndrew Rybchenko 	image_header.offset = cms_header.offset + cms_header.size;
64902d7c0c3SAndrew Rybchenko 	image_header.size = infop->eii_headerp->eih_size;
65002d7c0c3SAndrew Rybchenko 	if ((image_header.size > buffer_size) ||
65102d7c0c3SAndrew Rybchenko 	    (image_header.offset > (buffer_size - image_header.size))) {
65202d7c0c3SAndrew Rybchenko 		rc = EINVAL;
65302d7c0c3SAndrew Rybchenko 		goto fail4;
65402d7c0c3SAndrew Rybchenko 	}
65502d7c0c3SAndrew Rybchenko 
65602d7c0c3SAndrew Rybchenko 	code.offset = image_header.offset + image_header.size;
65702d7c0c3SAndrew Rybchenko 	code.size = infop->eii_headerp->eih_code_size;
65802d7c0c3SAndrew Rybchenko 	if ((code.size > buffer_size) ||
65902d7c0c3SAndrew Rybchenko 	    (code.offset > (buffer_size - code.size))) {
66002d7c0c3SAndrew Rybchenko 		rc = EINVAL;
66102d7c0c3SAndrew Rybchenko 		goto fail5;
66202d7c0c3SAndrew Rybchenko 	}
66302d7c0c3SAndrew Rybchenko 
66402d7c0c3SAndrew Rybchenko 	image_trailer.offset = code.offset + code.size;
66502d7c0c3SAndrew Rybchenko 	image_trailer.size = EFX_IMAGE_TRAILER_SIZE;
66602d7c0c3SAndrew Rybchenko 	if ((image_trailer.size > buffer_size) ||
66702d7c0c3SAndrew Rybchenko 	    (image_trailer.offset > (buffer_size - image_trailer.size))) {
66802d7c0c3SAndrew Rybchenko 		rc = EINVAL;
66902d7c0c3SAndrew Rybchenko 		goto fail6;
67002d7c0c3SAndrew Rybchenko 	}
67102d7c0c3SAndrew Rybchenko 
67202d7c0c3SAndrew Rybchenko 	signature.offset = image_trailer.offset + image_trailer.size;
67302d7c0c3SAndrew Rybchenko 	signature.size = (uint32_t)(infop->eii_image_size - signature.offset);
67402d7c0c3SAndrew Rybchenko 	if ((signature.size > buffer_size) ||
67502d7c0c3SAndrew Rybchenko 	    (signature.offset > (buffer_size - signature.size))) {
67602d7c0c3SAndrew Rybchenko 		rc = EINVAL;
67702d7c0c3SAndrew Rybchenko 		goto fail7;
67802d7c0c3SAndrew Rybchenko 	}
67902d7c0c3SAndrew Rybchenko 
68002d7c0c3SAndrew Rybchenko 	EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size +
68102d7c0c3SAndrew Rybchenko 	    image_header.size + code.size + image_trailer.size +
68202d7c0c3SAndrew Rybchenko 	    signature.size);
68302d7c0c3SAndrew Rybchenko 
68402d7c0c3SAndrew Rybchenko 	/* BEGIN CSTYLED */
68502d7c0c3SAndrew Rybchenko 	/*
68602d7c0c3SAndrew Rybchenko 	 * Build signed image partition, inserting chunk headers.
68702d7c0c3SAndrew Rybchenko 	 *
68802d7c0c3SAndrew Rybchenko 	 *  Signed Image:                  Image in NVRAM partition:
68902d7c0c3SAndrew Rybchenko 	 *
69002d7c0c3SAndrew Rybchenko 	 *  +-----------------+            +-----------------+
69102d7c0c3SAndrew Rybchenko 	 *  | CMS header      |            |  mcfw.update    |<----+
69202d7c0c3SAndrew Rybchenko 	 *  +-----------------+            |                 |     |
69302d7c0c3SAndrew Rybchenko 	 *  | reflash header  |            +-----------------+     |
69402d7c0c3SAndrew Rybchenko 	 *  +-----------------+            | chunk header:   |-->--|-+
69502d7c0c3SAndrew Rybchenko 	 *  | mcfw.update     |            | REFLASH_TRAILER |     | |
69602d7c0c3SAndrew Rybchenko 	 *  |                 |            +-----------------+     | |
69702d7c0c3SAndrew Rybchenko 	 *  +-----------------+        +-->| CMS header      |     | |
69802d7c0c3SAndrew Rybchenko 	 *  | reflash trailer |        |   +-----------------+     | |
69902d7c0c3SAndrew Rybchenko 	 *  +-----------------+        |   | chunk header:   |->-+ | |
70002d7c0c3SAndrew Rybchenko 	 *  | signature       |        |   | REFLASH_HEADER  |   | | |
70102d7c0c3SAndrew Rybchenko 	 *  +-----------------+        |   +-----------------+   | | |
70202d7c0c3SAndrew Rybchenko 	 *                             |   | reflash header  |<--+ | |
70302d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+     | |
70402d7c0c3SAndrew Rybchenko 	 *                             |   | chunk header:   |-->--+ |
70502d7c0c3SAndrew Rybchenko 	 *                             |   | IMAGE           |       |
70602d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+       |
70702d7c0c3SAndrew Rybchenko 	 *                             |   | reflash trailer |<------+
70802d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+
70902d7c0c3SAndrew Rybchenko 	 *                             |   | chunk header:   |
71002d7c0c3SAndrew Rybchenko 	 *                             |   | SIGNATURE       |->-+
71102d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+   |
71202d7c0c3SAndrew Rybchenko 	 *                             |   | signature       |<--+
71302d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+
71402d7c0c3SAndrew Rybchenko 	 *                             |   | ...unused...    |
71502d7c0c3SAndrew Rybchenko 	 *                             |   +-----------------+
71602d7c0c3SAndrew Rybchenko 	 *                             +-<-| chunk header:   |
71702d7c0c3SAndrew Rybchenko 	 *                             >-->| CMS_HEADER      |
71802d7c0c3SAndrew Rybchenko 	 *                                 +-----------------+
71902d7c0c3SAndrew Rybchenko 	 *
72002d7c0c3SAndrew Rybchenko 	 * Each chunk header gives the partition offset and length of the image
72102d7c0c3SAndrew Rybchenko 	 * chunk's data. The image chunk data is immediately followed by the
72202d7c0c3SAndrew Rybchenko 	 * chunk header for the next chunk.
72302d7c0c3SAndrew Rybchenko 	 *
72402d7c0c3SAndrew Rybchenko 	 * The data chunk for the firmware code must be at the start of the
72502d7c0c3SAndrew Rybchenko 	 * partition (needed for the bootloader). The first chunk header in the
72602d7c0c3SAndrew Rybchenko 	 * chain (for the CMS header) is stored at the end of the partition. The
72702d7c0c3SAndrew Rybchenko 	 * chain of chunk headers maintains the same logical order of image
72802d7c0c3SAndrew Rybchenko 	 * chunks as the original signed image file. This set of constraints
72902d7c0c3SAndrew Rybchenko 	 * results in the layout used for the data chunks and chunk headers.
73002d7c0c3SAndrew Rybchenko 	 */
73102d7c0c3SAndrew Rybchenko 	/* END CSTYLED */
73202d7c0c3SAndrew Rybchenko 	memset(bufferp, 0xFF, buffer_size);
73302d7c0c3SAndrew Rybchenko 
73402d7c0c3SAndrew Rybchenko 	EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN);
73502d7c0c3SAndrew Rybchenko 	memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN);
73602d7c0c3SAndrew Rybchenko 
73702d7c0c3SAndrew Rybchenko 	/*
73802d7c0c3SAndrew Rybchenko 	 * CMS header
73902d7c0c3SAndrew Rybchenko 	 */
74002d7c0c3SAndrew Rybchenko 	if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) {
74102d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
74202d7c0c3SAndrew Rybchenko 		goto fail8;
74302d7c0c3SAndrew Rybchenko 	}
74402d7c0c3SAndrew Rybchenko 	hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN;
74502d7c0c3SAndrew Rybchenko 
74602d7c0c3SAndrew Rybchenko 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
74702d7c0c3SAndrew Rybchenko 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
74802d7c0c3SAndrew Rybchenko 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_CMS_HEADER;
74902d7c0c3SAndrew Rybchenko 	chunk_hdr.offset	= code.size + SIGNED_IMAGE_CHUNK_HDR_LEN;
75002d7c0c3SAndrew Rybchenko 	chunk_hdr.len		= cms_header.size;
75102d7c0c3SAndrew Rybchenko 
75202d7c0c3SAndrew Rybchenko 	memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr));
75302d7c0c3SAndrew Rybchenko 
75402d7c0c3SAndrew Rybchenko 	if ((chunk_hdr.len > buffer_size) ||
75502d7c0c3SAndrew Rybchenko 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
75602d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
75702d7c0c3SAndrew Rybchenko 		goto fail9;
75802d7c0c3SAndrew Rybchenko 	}
75902d7c0c3SAndrew Rybchenko 	memcpy(bufferp + chunk_hdr.offset,
76002d7c0c3SAndrew Rybchenko 	    infop->eii_imagep + cms_header.offset,
76102d7c0c3SAndrew Rybchenko 	    cms_header.size);
76202d7c0c3SAndrew Rybchenko 
76302d7c0c3SAndrew Rybchenko 	/*
76402d7c0c3SAndrew Rybchenko 	 * Image header
76502d7c0c3SAndrew Rybchenko 	 */
76602d7c0c3SAndrew Rybchenko 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
76702d7c0c3SAndrew Rybchenko 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
76802d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
76902d7c0c3SAndrew Rybchenko 		goto fail10;
77002d7c0c3SAndrew Rybchenko 	}
77102d7c0c3SAndrew Rybchenko 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
77202d7c0c3SAndrew Rybchenko 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
77302d7c0c3SAndrew Rybchenko 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_REFLASH_HEADER;
77402d7c0c3SAndrew Rybchenko 	chunk_hdr.offset	= hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
77502d7c0c3SAndrew Rybchenko 	chunk_hdr.len		= image_header.size;
77602d7c0c3SAndrew Rybchenko 
77702d7c0c3SAndrew Rybchenko 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
77802d7c0c3SAndrew Rybchenko 
77902d7c0c3SAndrew Rybchenko 	if ((chunk_hdr.len > buffer_size) ||
78002d7c0c3SAndrew Rybchenko 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
78102d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
78202d7c0c3SAndrew Rybchenko 		goto fail11;
78302d7c0c3SAndrew Rybchenko 	}
78402d7c0c3SAndrew Rybchenko 	memcpy(bufferp + chunk_hdr.offset,
78502d7c0c3SAndrew Rybchenko 	    infop->eii_imagep + image_header.offset,
78602d7c0c3SAndrew Rybchenko 	    image_header.size);
78702d7c0c3SAndrew Rybchenko 
78802d7c0c3SAndrew Rybchenko 	*headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset);
78902d7c0c3SAndrew Rybchenko 
79002d7c0c3SAndrew Rybchenko 	/*
79102d7c0c3SAndrew Rybchenko 	 * Firmware code
79202d7c0c3SAndrew Rybchenko 	 */
79302d7c0c3SAndrew Rybchenko 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
79402d7c0c3SAndrew Rybchenko 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
79502d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
79602d7c0c3SAndrew Rybchenko 		goto fail12;
79702d7c0c3SAndrew Rybchenko 	}
79802d7c0c3SAndrew Rybchenko 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
79902d7c0c3SAndrew Rybchenko 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
80002d7c0c3SAndrew Rybchenko 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_IMAGE;
80102d7c0c3SAndrew Rybchenko 	chunk_hdr.offset	= 0;
80202d7c0c3SAndrew Rybchenko 	chunk_hdr.len		= code.size;
80302d7c0c3SAndrew Rybchenko 
80402d7c0c3SAndrew Rybchenko 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
80502d7c0c3SAndrew Rybchenko 
80602d7c0c3SAndrew Rybchenko 	if ((chunk_hdr.len > buffer_size) ||
80702d7c0c3SAndrew Rybchenko 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
80802d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
80902d7c0c3SAndrew Rybchenko 		goto fail13;
81002d7c0c3SAndrew Rybchenko 	}
81102d7c0c3SAndrew Rybchenko 	memcpy(bufferp + chunk_hdr.offset,
81202d7c0c3SAndrew Rybchenko 	    infop->eii_imagep + code.offset,
81302d7c0c3SAndrew Rybchenko 	    code.size);
81402d7c0c3SAndrew Rybchenko 
81502d7c0c3SAndrew Rybchenko 	/*
81602d7c0c3SAndrew Rybchenko 	 * Image trailer (CRC)
81702d7c0c3SAndrew Rybchenko 	 */
81802d7c0c3SAndrew Rybchenko 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
81902d7c0c3SAndrew Rybchenko 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
82002d7c0c3SAndrew Rybchenko 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_REFLASH_TRAILER;
82102d7c0c3SAndrew Rybchenko 	chunk_hdr.offset	= hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
82202d7c0c3SAndrew Rybchenko 	chunk_hdr.len		= image_trailer.size;
82302d7c0c3SAndrew Rybchenko 
82402d7c0c3SAndrew Rybchenko 	hdr_offset = code.size;
82502d7c0c3SAndrew Rybchenko 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
82602d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
82702d7c0c3SAndrew Rybchenko 		goto fail14;
82802d7c0c3SAndrew Rybchenko 	}
82902d7c0c3SAndrew Rybchenko 
83002d7c0c3SAndrew Rybchenko 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
83102d7c0c3SAndrew Rybchenko 
83202d7c0c3SAndrew Rybchenko 	if ((chunk_hdr.len > buffer_size) ||
83302d7c0c3SAndrew Rybchenko 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
83402d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
83502d7c0c3SAndrew Rybchenko 		goto fail15;
83602d7c0c3SAndrew Rybchenko 	}
83702d7c0c3SAndrew Rybchenko 	memcpy((uint8_t *)bufferp + chunk_hdr.offset,
83802d7c0c3SAndrew Rybchenko 	    infop->eii_imagep + image_trailer.offset,
83902d7c0c3SAndrew Rybchenko 	    image_trailer.size);
84002d7c0c3SAndrew Rybchenko 
84102d7c0c3SAndrew Rybchenko 	/*
84202d7c0c3SAndrew Rybchenko 	 * Signature
84302d7c0c3SAndrew Rybchenko 	 */
84402d7c0c3SAndrew Rybchenko 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
84502d7c0c3SAndrew Rybchenko 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
84602d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
84702d7c0c3SAndrew Rybchenko 		goto fail16;
84802d7c0c3SAndrew Rybchenko 	}
84902d7c0c3SAndrew Rybchenko 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
85002d7c0c3SAndrew Rybchenko 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
85102d7c0c3SAndrew Rybchenko 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_SIGNATURE;
85202d7c0c3SAndrew Rybchenko 	chunk_hdr.offset	= chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
85302d7c0c3SAndrew Rybchenko 	chunk_hdr.len		= signature.size;
85402d7c0c3SAndrew Rybchenko 
85502d7c0c3SAndrew Rybchenko 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
85602d7c0c3SAndrew Rybchenko 
85702d7c0c3SAndrew Rybchenko 	if ((chunk_hdr.len > buffer_size) ||
85802d7c0c3SAndrew Rybchenko 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
85902d7c0c3SAndrew Rybchenko 		rc = ENOSPC;
86002d7c0c3SAndrew Rybchenko 		goto fail17;
86102d7c0c3SAndrew Rybchenko 	}
86202d7c0c3SAndrew Rybchenko 	memcpy(bufferp + chunk_hdr.offset,
86302d7c0c3SAndrew Rybchenko 	    infop->eii_imagep + signature.offset,
86402d7c0c3SAndrew Rybchenko 	    signature.size);
86502d7c0c3SAndrew Rybchenko 
86602d7c0c3SAndrew Rybchenko 	return (0);
86702d7c0c3SAndrew Rybchenko 
86802d7c0c3SAndrew Rybchenko fail17:
86902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail17);
87002d7c0c3SAndrew Rybchenko fail16:
87102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail16);
87202d7c0c3SAndrew Rybchenko fail15:
87302d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail15);
87402d7c0c3SAndrew Rybchenko fail14:
87502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail14);
87602d7c0c3SAndrew Rybchenko fail13:
87702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail13);
87802d7c0c3SAndrew Rybchenko fail12:
87902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail12);
88002d7c0c3SAndrew Rybchenko fail11:
88102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail11);
88202d7c0c3SAndrew Rybchenko fail10:
88302d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail10);
88402d7c0c3SAndrew Rybchenko fail9:
88502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail9);
88602d7c0c3SAndrew Rybchenko fail8:
88702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail8);
88802d7c0c3SAndrew Rybchenko fail7:
88902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail7);
89002d7c0c3SAndrew Rybchenko fail6:
89102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail6);
89202d7c0c3SAndrew Rybchenko fail5:
89302d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail5);
89402d7c0c3SAndrew Rybchenko fail4:
89502d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail4);
89602d7c0c3SAndrew Rybchenko fail3:
89702d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail3);
89802d7c0c3SAndrew Rybchenko fail2:
89902d7c0c3SAndrew Rybchenko 	EFSYS_PROBE(fail2);
90002d7c0c3SAndrew Rybchenko fail1:
90102d7c0c3SAndrew Rybchenko 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
90202d7c0c3SAndrew Rybchenko 
90302d7c0c3SAndrew Rybchenko 	return (rc);
90402d7c0c3SAndrew Rybchenko }
905391763d7SAndrew Rybchenko 
906391763d7SAndrew Rybchenko #endif	/* EFSYS_OPT_IMAGE_LAYOUT */
907391763d7SAndrew Rybchenko 
908391763d7SAndrew Rybchenko #endif	/* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
909