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