xref: /freebsd/sys/dev/sfxge/common/ef10_image.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2017-2018 Solarflare Communications Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice,
11  *    this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  *    this list of conditions and the following disclaimer in the documentation
14  *    and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * The views and conclusions contained in the software and documentation are
29  * those of the authors and should not be interpreted as representing official
30  * policies, either expressed or implied, of the FreeBSD Project.
31  */
32 
33 #include <sys/cdefs.h>
34 #include "efx.h"
35 #include "efx_impl.h"
36 
37 #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
38 
39 #if EFSYS_OPT_IMAGE_LAYOUT
40 
41 /*
42  * Utility routines to support limited parsing of ASN.1 tags. This is not a
43  * general purpose ASN.1 parser, but is sufficient to locate the required
44  * objects in a signed image with CMS headers.
45  */
46 
47 /* DER encodings for ASN.1 tags (see ITU-T X.690) */
48 #define	ASN1_TAG_INTEGER	    (0x02)
49 #define	ASN1_TAG_OCTET_STRING	    (0x04)
50 #define	ASN1_TAG_OBJ_ID		    (0x06)
51 #define	ASN1_TAG_SEQUENCE	    (0x30)
52 #define	ASN1_TAG_SET		    (0x31)
53 
54 #define	ASN1_TAG_IS_PRIM(tag)	    ((tag & 0x20) == 0)
55 
56 #define	ASN1_TAG_PRIM_CONTEXT(n)    (0x80 + (n))
57 #define	ASN1_TAG_CONS_CONTEXT(n)    (0xA0 + (n))
58 
59 typedef struct efx_asn1_cursor_s {
60 	uint8_t		*buffer;
61 	uint32_t	length;
62 
63 	uint8_t		tag;
64 	uint32_t	hdr_size;
65 	uint32_t	val_size;
66 } efx_asn1_cursor_t;
67 
68 /* Parse header of DER encoded ASN.1 TLV and match tag */
69 static	__checkReturn	efx_rc_t
70 efx_asn1_parse_header_match_tag(
71 	__inout		efx_asn1_cursor_t	*cursor,
72 	__in		uint8_t			tag)
73 {
74 	efx_rc_t rc;
75 
76 	if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) {
77 		rc = EINVAL;
78 		goto fail1;
79 	}
80 
81 	cursor->tag = cursor->buffer[0];
82 	if (cursor->tag != tag) {
83 		/* Tag not matched */
84 		rc = ENOENT;
85 		goto fail2;
86 	}
87 
88 	if ((cursor->tag & 0x1F) == 0x1F) {
89 		/* Long tag format not used in CMS syntax */
90 		rc = EINVAL;
91 		goto fail3;
92 	}
93 
94 	if ((cursor->buffer[1] & 0x80) == 0) {
95 		/* Short form: length is 0..127 */
96 		cursor->hdr_size = 2;
97 		cursor->val_size = cursor->buffer[1];
98 	} else {
99 		/* Long form: length encoded as [0x80+nbytes][length bytes] */
100 		uint32_t nbytes = cursor->buffer[1] & 0x7F;
101 		uint32_t offset;
102 
103 		if (nbytes == 0) {
104 			/* Indefinite length not allowed in DER encoding */
105 			rc = EINVAL;
106 			goto fail4;
107 		}
108 		if (2 + nbytes > cursor->length) {
109 			/* Header length overflows image buffer */
110 			rc = EINVAL;
111 			goto fail6;
112 		}
113 		if (nbytes > sizeof (uint32_t)) {
114 			/* Length encoding too big */
115 			rc = E2BIG;
116 			goto fail5;
117 		}
118 		cursor->hdr_size = 2 + nbytes;
119 		cursor->val_size = 0;
120 		for (offset = 2; offset < cursor->hdr_size; offset++) {
121 			cursor->val_size =
122 			    (cursor->val_size << 8) | cursor->buffer[offset];
123 		}
124 	}
125 
126 	if ((cursor->hdr_size + cursor->val_size) > cursor->length) {
127 		/* Length overflows image buffer */
128 		rc = E2BIG;
129 		goto fail7;
130 	}
131 
132 	return (0);
133 
134 fail7:
135 	EFSYS_PROBE(fail7);
136 fail6:
137 	EFSYS_PROBE(fail6);
138 fail5:
139 	EFSYS_PROBE(fail5);
140 fail4:
141 	EFSYS_PROBE(fail4);
142 fail3:
143 	EFSYS_PROBE(fail3);
144 fail2:
145 	EFSYS_PROBE(fail2);
146 fail1:
147 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
148 
149 	return (rc);
150 }
151 
152 /* Enter nested ASN.1 TLV (contained in value of current TLV) */
153 static	__checkReturn	efx_rc_t
154 efx_asn1_enter_tag(
155 	__inout		efx_asn1_cursor_t	*cursor,
156 	__in		uint8_t			tag)
157 {
158 	efx_rc_t rc;
159 
160 	if (cursor == NULL) {
161 		rc = EINVAL;
162 		goto fail1;
163 	}
164 
165 	if (ASN1_TAG_IS_PRIM(tag)) {
166 		/* Cannot enter a primitive tag */
167 		rc = ENOTSUP;
168 		goto fail2;
169 	}
170 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
171 	if (rc != 0) {
172 		/* Invalid TLV or wrong tag */
173 		goto fail3;
174 	}
175 
176 	/* Limit cursor range to nested TLV */
177 	cursor->buffer += cursor->hdr_size;
178 	cursor->length = cursor->val_size;
179 
180 	return (0);
181 
182 fail3:
183 	EFSYS_PROBE(fail3);
184 fail2:
185 	EFSYS_PROBE(fail2);
186 fail1:
187 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
188 
189 	return (rc);
190 }
191 
192 /*
193  * Check that the current ASN.1 TLV matches the given tag and value.
194  * Advance cursor to next TLV on a successful match.
195  */
196 static	__checkReturn	efx_rc_t
197 efx_asn1_match_tag_value(
198 	__inout		efx_asn1_cursor_t	*cursor,
199 	__in		uint8_t			tag,
200 	__in		const void		*valp,
201 	__in		uint32_t		val_size)
202 {
203 	efx_rc_t rc;
204 
205 	if (cursor == NULL) {
206 		rc = EINVAL;
207 		goto fail1;
208 	}
209 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
210 	if (rc != 0) {
211 		/* Invalid TLV or wrong tag */
212 		goto fail2;
213 	}
214 	if (cursor->val_size != val_size) {
215 		/* Value size is different */
216 		rc = EINVAL;
217 		goto fail3;
218 	}
219 	if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) {
220 		/* Value content is different */
221 		rc = EINVAL;
222 		goto fail4;
223 	}
224 	cursor->buffer += cursor->hdr_size + cursor->val_size;
225 	cursor->length -= cursor->hdr_size + cursor->val_size;
226 
227 	return (0);
228 
229 fail4:
230 	EFSYS_PROBE(fail4);
231 fail3:
232 	EFSYS_PROBE(fail3);
233 fail2:
234 	EFSYS_PROBE(fail2);
235 fail1:
236 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
237 
238 	return (rc);
239 }
240 
241 /* Advance cursor to next TLV */
242 static	__checkReturn	efx_rc_t
243 efx_asn1_skip_tag(
244 	__inout		efx_asn1_cursor_t	*cursor,
245 	__in		uint8_t			tag)
246 {
247 	efx_rc_t rc;
248 
249 	if (cursor == NULL) {
250 		rc = EINVAL;
251 		goto fail1;
252 	}
253 
254 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
255 	if (rc != 0) {
256 		/* Invalid TLV or wrong tag */
257 		goto fail2;
258 	}
259 	cursor->buffer += cursor->hdr_size + cursor->val_size;
260 	cursor->length -= cursor->hdr_size + cursor->val_size;
261 
262 	return (0);
263 
264 fail2:
265 	EFSYS_PROBE(fail2);
266 fail1:
267 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
268 
269 	return (rc);
270 }
271 
272 /* Return pointer to value octets and value size from current TLV */
273 static	__checkReturn	efx_rc_t
274 efx_asn1_get_tag_value(
275 	__inout		efx_asn1_cursor_t	*cursor,
276 	__in		uint8_t			tag,
277 	__out		uint8_t			**valp,
278 	__out		uint32_t		*val_sizep)
279 {
280 	efx_rc_t rc;
281 
282 	if (cursor == NULL || valp == NULL || val_sizep == NULL) {
283 		rc = EINVAL;
284 		goto fail1;
285 	}
286 
287 	rc = efx_asn1_parse_header_match_tag(cursor, tag);
288 	if (rc != 0) {
289 		/* Invalid TLV or wrong tag */
290 		goto fail2;
291 	}
292 	*valp = cursor->buffer + cursor->hdr_size;
293 	*val_sizep = cursor->val_size;
294 
295 	return (0);
296 
297 fail2:
298 	EFSYS_PROBE(fail2);
299 fail1:
300 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
301 
302 	return (rc);
303 }
304 
305 /*
306  * Utility routines for parsing CMS headers (see RFC2315, PKCS#7)
307  */
308 
309 /* OID 1.2.840.113549.1.7.2 */
310 static const uint8_t PKCS7_SignedData[] =
311 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
312 
313 /* OID 1.2.840.113549.1.7.1 */
314 static const uint8_t PKCS7_Data[] =
315 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
316 
317 /* SignedData structure version */
318 static const uint8_t SignedData_Version[] =
319 { 0x03 };
320 
321 /*
322  * Check for a valid image in signed image format. This uses CMS syntax
323  * (see RFC2315, PKCS#7) to provide signatures, and certificates required
324  * to validate the signatures. The encapsulated content is in unsigned image
325  * format (reflash header, image code, trailer checksum).
326  */
327 static	__checkReturn	efx_rc_t
328 efx_check_signed_image_header(
329 	__in		void		*bufferp,
330 	__in		uint32_t	buffer_size,
331 	__out		uint32_t	*content_offsetp,
332 	__out		uint32_t	*content_lengthp)
333 {
334 	efx_asn1_cursor_t cursor;
335 	uint8_t *valp;
336 	uint32_t val_size;
337 	efx_rc_t rc;
338 
339 	if (content_offsetp == NULL || content_lengthp == NULL) {
340 		rc = EINVAL;
341 		goto fail1;
342 	}
343 	cursor.buffer = (uint8_t *)bufferp;
344 	cursor.length = buffer_size;
345 
346 	/* ContextInfo */
347 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
348 	if (rc != 0)
349 		goto fail2;
350 
351 	/* ContextInfo.contentType */
352 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
353 	    PKCS7_SignedData, sizeof (PKCS7_SignedData));
354 	if (rc != 0)
355 		goto fail3;
356 
357 	/* ContextInfo.content */
358 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
359 	if (rc != 0)
360 		goto fail4;
361 
362 	/* SignedData */
363 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
364 	if (rc != 0)
365 		goto fail5;
366 
367 	/* SignedData.version */
368 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER,
369 	    SignedData_Version, sizeof (SignedData_Version));
370 	if (rc != 0)
371 		goto fail6;
372 
373 	/* SignedData.digestAlgorithms */
374 	rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET);
375 	if (rc != 0)
376 		goto fail7;
377 
378 	/* SignedData.encapContentInfo */
379 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
380 	if (rc != 0)
381 		goto fail8;
382 
383 	/* SignedData.encapContentInfo.econtentType */
384 	rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
385 	    PKCS7_Data, sizeof (PKCS7_Data));
386 	if (rc != 0)
387 		goto fail9;
388 
389 	/* SignedData.encapContentInfo.econtent */
390 	rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
391 	if (rc != 0)
392 		goto fail10;
393 
394 	/*
395 	 * The octet string contains the image header, image code bytes and
396 	 * image trailer CRC (same as unsigned image layout).
397 	 */
398 	valp = NULL;
399 	val_size = 0;
400 	rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING,
401 	    &valp, &val_size);
402 	if (rc != 0)
403 		goto fail11;
404 
405 	if ((valp == NULL) || (val_size == 0)) {
406 		rc = EINVAL;
407 		goto fail12;
408 	}
409 	if (valp < (uint8_t *)bufferp) {
410 		rc = EINVAL;
411 		goto fail13;
412 	}
413 	if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) {
414 		rc = EINVAL;
415 		goto fail14;
416 	}
417 
418 	*content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp);
419 	*content_lengthp = val_size;
420 
421 	return (0);
422 
423 fail14:
424 	EFSYS_PROBE(fail14);
425 fail13:
426 	EFSYS_PROBE(fail13);
427 fail12:
428 	EFSYS_PROBE(fail12);
429 fail11:
430 	EFSYS_PROBE(fail11);
431 fail10:
432 	EFSYS_PROBE(fail10);
433 fail9:
434 	EFSYS_PROBE(fail9);
435 fail8:
436 	EFSYS_PROBE(fail8);
437 fail7:
438 	EFSYS_PROBE(fail7);
439 fail6:
440 	EFSYS_PROBE(fail6);
441 fail5:
442 	EFSYS_PROBE(fail5);
443 fail4:
444 	EFSYS_PROBE(fail4);
445 fail3:
446 	EFSYS_PROBE(fail3);
447 fail2:
448 	EFSYS_PROBE(fail2);
449 fail1:
450 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
451 
452 	return (rc);
453 }
454 
455 static	__checkReturn	efx_rc_t
456 efx_check_unsigned_image(
457 	__in		void		*bufferp,
458 	__in		uint32_t	buffer_size)
459 {
460 	efx_image_header_t *header;
461 	efx_image_trailer_t *trailer;
462 	uint32_t crc;
463 	efx_rc_t rc;
464 
465 	EFX_STATIC_ASSERT(sizeof (*header) == EFX_IMAGE_HEADER_SIZE);
466 	EFX_STATIC_ASSERT(sizeof (*trailer) == EFX_IMAGE_TRAILER_SIZE);
467 
468 	/* Must have at least enough space for required image header fields */
469 	if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) +
470 		sizeof (header->eih_size))) {
471 		rc = ENOSPC;
472 		goto fail1;
473 	}
474 	header = (efx_image_header_t *)bufferp;
475 
476 	if (header->eih_magic != EFX_IMAGE_HEADER_MAGIC) {
477 		rc = EINVAL;
478 		goto fail2;
479 	}
480 
481 	/*
482 	 * Check image header version is same or higher than lowest required
483 	 * version.
484 	 */
485 	if (header->eih_version < EFX_IMAGE_HEADER_VERSION) {
486 		rc = EINVAL;
487 		goto fail3;
488 	}
489 
490 	/* Buffer must have space for image header, code and image trailer. */
491 	if (buffer_size < (header->eih_size + header->eih_code_size +
492 		EFX_IMAGE_TRAILER_SIZE)) {
493 		rc = ENOSPC;
494 		goto fail4;
495 	}
496 
497 	/* Check CRC from image buffer matches computed CRC. */
498 	trailer = (efx_image_trailer_t *)((uint8_t *)header +
499 	    header->eih_size + header->eih_code_size);
500 
501 	crc = efx_crc32_calculate(0, (uint8_t *)header,
502 	    (header->eih_size + header->eih_code_size));
503 
504 	if (trailer->eit_crc != crc) {
505 		rc = EINVAL;
506 		goto fail5;
507 	}
508 
509 	return (0);
510 
511 fail5:
512 	EFSYS_PROBE(fail5);
513 fail4:
514 	EFSYS_PROBE(fail4);
515 fail3:
516 	EFSYS_PROBE(fail3);
517 fail2:
518 	EFSYS_PROBE(fail2);
519 fail1:
520 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
521 
522 	return (rc);
523 }
524 
525 	__checkReturn	efx_rc_t
526 efx_check_reflash_image(
527 	__in		void			*bufferp,
528 	__in		uint32_t		buffer_size,
529 	__out		efx_image_info_t	*infop)
530 {
531 	efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE;
532 	uint32_t image_offset;
533 	uint32_t image_size;
534 	void *imagep;
535 	efx_rc_t rc;
536 
537 	EFSYS_ASSERT(infop != NULL);
538 	if (infop == NULL) {
539 		rc = EINVAL;
540 		goto fail1;
541 	}
542 	memset(infop, 0, sizeof (*infop));
543 
544 	if (bufferp == NULL || buffer_size == 0) {
545 		rc = EINVAL;
546 		goto fail2;
547 	}
548 
549 	/*
550 	 * Check if the buffer contains an image in signed format, and if so,
551 	 * locate the image header.
552 	 */
553 	rc = efx_check_signed_image_header(bufferp, buffer_size,
554 	    &image_offset, &image_size);
555 	if (rc == 0) {
556 		/*
557 		 * Buffer holds signed image format. Check that the encapsulated
558 		 * content is in unsigned image format.
559 		 */
560 		format = EFX_IMAGE_FORMAT_SIGNED;
561 	} else {
562 		/* Check if the buffer holds image in unsigned image format */
563 		format = EFX_IMAGE_FORMAT_UNSIGNED;
564 		image_offset = 0;
565 		image_size = buffer_size;
566 	}
567 	if (image_offset + image_size > buffer_size) {
568 		rc = E2BIG;
569 		goto fail3;
570 	}
571 	imagep = (uint8_t *)bufferp + image_offset;
572 
573 	/* Check unsigned image layout (image header, code, image trailer) */
574 	rc = efx_check_unsigned_image(imagep, image_size);
575 	if (rc != 0)
576 		goto fail4;
577 
578 	/* Return image details */
579 	infop->eii_format = format;
580 	infop->eii_imagep = bufferp;
581 	infop->eii_image_size = buffer_size;
582 	infop->eii_headerp = (efx_image_header_t *)imagep;
583 
584 	return (0);
585 
586 fail4:
587 	EFSYS_PROBE(fail4);
588 fail3:
589 	EFSYS_PROBE(fail3);
590 fail2:
591 	EFSYS_PROBE(fail2);
592 	infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
593 	infop->eii_imagep = NULL;
594 	infop->eii_image_size = 0;
595 
596 fail1:
597 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
598 
599 	return (rc);
600 }
601 
602 	__checkReturn	efx_rc_t
603 efx_build_signed_image_write_buffer(
604 	__out_bcount(buffer_size)
605 			uint8_t			*bufferp,
606 	__in		uint32_t		buffer_size,
607 	__in		efx_image_info_t	*infop,
608 	__out		efx_image_header_t	**headerpp)
609 {
610 	signed_image_chunk_hdr_t chunk_hdr;
611 	uint32_t hdr_offset;
612 	struct {
613 		uint32_t offset;
614 		uint32_t size;
615 	} cms_header, image_header, code, image_trailer, signature;
616 	efx_rc_t rc;
617 
618 	EFSYS_ASSERT((infop != NULL) && (headerpp != NULL));
619 
620 	if ((bufferp == NULL) || (buffer_size == 0) ||
621 	    (infop == NULL) || (headerpp == NULL)) {
622 		/* Invalid arguments */
623 		rc = EINVAL;
624 		goto fail1;
625 	}
626 	if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) ||
627 	    (infop->eii_imagep == NULL) ||
628 	    (infop->eii_headerp == NULL) ||
629 	    ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) ||
630 	    (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) ||
631 	    ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) >
632 	    (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) {
633 		/* Invalid image info */
634 		rc = EINVAL;
635 		goto fail2;
636 	}
637 
638 	/* Locate image chunks in original signed image */
639 	cms_header.offset = 0;
640 	cms_header.size =
641 	    (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep);
642 	if ((cms_header.size > buffer_size) ||
643 	    (cms_header.offset > (buffer_size - cms_header.size))) {
644 		rc = EINVAL;
645 		goto fail3;
646 	}
647 
648 	image_header.offset = cms_header.offset + cms_header.size;
649 	image_header.size = infop->eii_headerp->eih_size;
650 	if ((image_header.size > buffer_size) ||
651 	    (image_header.offset > (buffer_size - image_header.size))) {
652 		rc = EINVAL;
653 		goto fail4;
654 	}
655 
656 	code.offset = image_header.offset + image_header.size;
657 	code.size = infop->eii_headerp->eih_code_size;
658 	if ((code.size > buffer_size) ||
659 	    (code.offset > (buffer_size - code.size))) {
660 		rc = EINVAL;
661 		goto fail5;
662 	}
663 
664 	image_trailer.offset = code.offset + code.size;
665 	image_trailer.size = EFX_IMAGE_TRAILER_SIZE;
666 	if ((image_trailer.size > buffer_size) ||
667 	    (image_trailer.offset > (buffer_size - image_trailer.size))) {
668 		rc = EINVAL;
669 		goto fail6;
670 	}
671 
672 	signature.offset = image_trailer.offset + image_trailer.size;
673 	signature.size = (uint32_t)(infop->eii_image_size - signature.offset);
674 	if ((signature.size > buffer_size) ||
675 	    (signature.offset > (buffer_size - signature.size))) {
676 		rc = EINVAL;
677 		goto fail7;
678 	}
679 
680 	EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size +
681 	    image_header.size + code.size + image_trailer.size +
682 	    signature.size);
683 
684 	/* BEGIN CSTYLED */
685 	/*
686 	 * Build signed image partition, inserting chunk headers.
687 	 *
688 	 *  Signed Image:                  Image in NVRAM partition:
689 	 *
690 	 *  +-----------------+            +-----------------+
691 	 *  | CMS header      |            |  mcfw.update    |<----+
692 	 *  +-----------------+            |                 |     |
693 	 *  | reflash header  |            +-----------------+     |
694 	 *  +-----------------+            | chunk header:   |-->--|-+
695 	 *  | mcfw.update     |            | REFLASH_TRAILER |     | |
696 	 *  |                 |            +-----------------+     | |
697 	 *  +-----------------+        +-->| CMS header      |     | |
698 	 *  | reflash trailer |        |   +-----------------+     | |
699 	 *  +-----------------+        |   | chunk header:   |->-+ | |
700 	 *  | signature       |        |   | REFLASH_HEADER  |   | | |
701 	 *  +-----------------+        |   +-----------------+   | | |
702 	 *                             |   | reflash header  |<--+ | |
703 	 *                             |   +-----------------+     | |
704 	 *                             |   | chunk header:   |-->--+ |
705 	 *                             |   | IMAGE           |       |
706 	 *                             |   +-----------------+       |
707 	 *                             |   | reflash trailer |<------+
708 	 *                             |   +-----------------+
709 	 *                             |   | chunk header:   |
710 	 *                             |   | SIGNATURE       |->-+
711 	 *                             |   +-----------------+   |
712 	 *                             |   | signature       |<--+
713 	 *                             |   +-----------------+
714 	 *                             |   | ...unused...    |
715 	 *                             |   +-----------------+
716 	 *                             +-<-| chunk header:   |
717 	 *                             >-->| CMS_HEADER      |
718 	 *                                 +-----------------+
719 	 *
720 	 * Each chunk header gives the partition offset and length of the image
721 	 * chunk's data. The image chunk data is immediately followed by the
722 	 * chunk header for the next chunk.
723 	 *
724 	 * The data chunk for the firmware code must be at the start of the
725 	 * partition (needed for the bootloader). The first chunk header in the
726 	 * chain (for the CMS header) is stored at the end of the partition. The
727 	 * chain of chunk headers maintains the same logical order of image
728 	 * chunks as the original signed image file. This set of constraints
729 	 * results in the layout used for the data chunks and chunk headers.
730 	 */
731 	/* END CSTYLED */
732 	memset(bufferp, 0xFF, buffer_size);
733 
734 	EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN);
735 	memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN);
736 
737 	/*
738 	 * CMS header
739 	 */
740 	if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) {
741 		rc = ENOSPC;
742 		goto fail8;
743 	}
744 	hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN;
745 
746 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
747 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
748 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_CMS_HEADER;
749 	chunk_hdr.offset	= code.size + SIGNED_IMAGE_CHUNK_HDR_LEN;
750 	chunk_hdr.len		= cms_header.size;
751 
752 	memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr));
753 
754 	if ((chunk_hdr.len > buffer_size) ||
755 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
756 		rc = ENOSPC;
757 		goto fail9;
758 	}
759 	memcpy(bufferp + chunk_hdr.offset,
760 	    infop->eii_imagep + cms_header.offset,
761 	    cms_header.size);
762 
763 	/*
764 	 * Image header
765 	 */
766 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
767 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
768 		rc = ENOSPC;
769 		goto fail10;
770 	}
771 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
772 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
773 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_REFLASH_HEADER;
774 	chunk_hdr.offset	= hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
775 	chunk_hdr.len		= image_header.size;
776 
777 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
778 
779 	if ((chunk_hdr.len > buffer_size) ||
780 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
781 		rc = ENOSPC;
782 		goto fail11;
783 	}
784 	memcpy(bufferp + chunk_hdr.offset,
785 	    infop->eii_imagep + image_header.offset,
786 	    image_header.size);
787 
788 	*headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset);
789 
790 	/*
791 	 * Firmware code
792 	 */
793 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
794 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
795 		rc = ENOSPC;
796 		goto fail12;
797 	}
798 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
799 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
800 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_IMAGE;
801 	chunk_hdr.offset	= 0;
802 	chunk_hdr.len		= code.size;
803 
804 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
805 
806 	if ((chunk_hdr.len > buffer_size) ||
807 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
808 		rc = ENOSPC;
809 		goto fail13;
810 	}
811 	memcpy(bufferp + chunk_hdr.offset,
812 	    infop->eii_imagep + code.offset,
813 	    code.size);
814 
815 	/*
816 	 * Image trailer (CRC)
817 	 */
818 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
819 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
820 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_REFLASH_TRAILER;
821 	chunk_hdr.offset	= hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
822 	chunk_hdr.len		= image_trailer.size;
823 
824 	hdr_offset = code.size;
825 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
826 		rc = ENOSPC;
827 		goto fail14;
828 	}
829 
830 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
831 
832 	if ((chunk_hdr.len > buffer_size) ||
833 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
834 		rc = ENOSPC;
835 		goto fail15;
836 	}
837 	memcpy((uint8_t *)bufferp + chunk_hdr.offset,
838 	    infop->eii_imagep + image_trailer.offset,
839 	    image_trailer.size);
840 
841 	/*
842 	 * Signature
843 	 */
844 	hdr_offset = chunk_hdr.offset + chunk_hdr.len;
845 	if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
846 		rc = ENOSPC;
847 		goto fail16;
848 	}
849 	chunk_hdr.magic		= SIGNED_IMAGE_CHUNK_HDR_MAGIC;
850 	chunk_hdr.version	= SIGNED_IMAGE_CHUNK_HDR_VERSION;
851 	chunk_hdr.id		= SIGNED_IMAGE_CHUNK_SIGNATURE;
852 	chunk_hdr.offset	= chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
853 	chunk_hdr.len		= signature.size;
854 
855 	memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
856 
857 	if ((chunk_hdr.len > buffer_size) ||
858 	    (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
859 		rc = ENOSPC;
860 		goto fail17;
861 	}
862 	memcpy(bufferp + chunk_hdr.offset,
863 	    infop->eii_imagep + signature.offset,
864 	    signature.size);
865 
866 	return (0);
867 
868 fail17:
869 	EFSYS_PROBE(fail17);
870 fail16:
871 	EFSYS_PROBE(fail16);
872 fail15:
873 	EFSYS_PROBE(fail15);
874 fail14:
875 	EFSYS_PROBE(fail14);
876 fail13:
877 	EFSYS_PROBE(fail13);
878 fail12:
879 	EFSYS_PROBE(fail12);
880 fail11:
881 	EFSYS_PROBE(fail11);
882 fail10:
883 	EFSYS_PROBE(fail10);
884 fail9:
885 	EFSYS_PROBE(fail9);
886 fail8:
887 	EFSYS_PROBE(fail8);
888 fail7:
889 	EFSYS_PROBE(fail7);
890 fail6:
891 	EFSYS_PROBE(fail6);
892 fail5:
893 	EFSYS_PROBE(fail5);
894 fail4:
895 	EFSYS_PROBE(fail4);
896 fail3:
897 	EFSYS_PROBE(fail3);
898 fail2:
899 	EFSYS_PROBE(fail2);
900 fail1:
901 	EFSYS_PROBE1(fail1, efx_rc_t, rc);
902 
903 	return (rc);
904 }
905 
906 #endif	/* EFSYS_OPT_IMAGE_LAYOUT */
907 
908 #endif	/* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
909