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