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
59typedef 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 */
69static	__checkReturn	efx_rc_t
70efx_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
134fail7:
135	EFSYS_PROBE(fail7);
136fail6:
137	EFSYS_PROBE(fail6);
138fail5:
139	EFSYS_PROBE(fail5);
140fail4:
141	EFSYS_PROBE(fail4);
142fail3:
143	EFSYS_PROBE(fail3);
144fail2:
145	EFSYS_PROBE(fail2);
146fail1:
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) */
153static	__checkReturn	efx_rc_t
154efx_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
182fail3:
183	EFSYS_PROBE(fail3);
184fail2:
185	EFSYS_PROBE(fail2);
186fail1:
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 */
196static	__checkReturn	efx_rc_t
197efx_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
229fail4:
230	EFSYS_PROBE(fail4);
231fail3:
232	EFSYS_PROBE(fail3);
233fail2:
234	EFSYS_PROBE(fail2);
235fail1:
236	EFSYS_PROBE1(fail1, efx_rc_t, rc);
237
238	return (rc);
239}
240
241/* Advance cursor to next TLV */
242static	__checkReturn	efx_rc_t
243efx_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
264fail2:
265	EFSYS_PROBE(fail2);
266fail1:
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 */
273static	__checkReturn	efx_rc_t
274efx_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
297fail2:
298	EFSYS_PROBE(fail2);
299fail1:
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 */
310static 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 */
314static const uint8_t PKCS7_Data[] =
315{ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
316
317/* SignedData structure version */
318static 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 */
327static	__checkReturn	efx_rc_t
328efx_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
423fail14:
424	EFSYS_PROBE(fail14);
425fail13:
426	EFSYS_PROBE(fail13);
427fail12:
428	EFSYS_PROBE(fail12);
429fail11:
430	EFSYS_PROBE(fail11);
431fail10:
432	EFSYS_PROBE(fail10);
433fail9:
434	EFSYS_PROBE(fail9);
435fail8:
436	EFSYS_PROBE(fail8);
437fail7:
438	EFSYS_PROBE(fail7);
439fail6:
440	EFSYS_PROBE(fail6);
441fail5:
442	EFSYS_PROBE(fail5);
443fail4:
444	EFSYS_PROBE(fail4);
445fail3:
446	EFSYS_PROBE(fail3);
447fail2:
448	EFSYS_PROBE(fail2);
449fail1:
450	EFSYS_PROBE1(fail1, efx_rc_t, rc);
451
452	return (rc);
453}
454
455static	__checkReturn	efx_rc_t
456efx_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
511fail5:
512	EFSYS_PROBE(fail5);
513fail4:
514	EFSYS_PROBE(fail4);
515fail3:
516	EFSYS_PROBE(fail3);
517fail2:
518	EFSYS_PROBE(fail2);
519fail1:
520	EFSYS_PROBE1(fail1, efx_rc_t, rc);
521
522	return (rc);
523}
524
525	__checkReturn	efx_rc_t
526efx_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
586fail4:
587	EFSYS_PROBE(fail4);
588fail3:
589	EFSYS_PROBE(fail3);
590fail2:
591	EFSYS_PROBE(fail2);
592	infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
593	infop->eii_imagep = NULL;
594	infop->eii_image_size = 0;
595
596fail1:
597	EFSYS_PROBE1(fail1, efx_rc_t, rc);
598
599	return (rc);
600}
601
602	__checkReturn	efx_rc_t
603efx_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
868fail17:
869	EFSYS_PROBE(fail17);
870fail16:
871	EFSYS_PROBE(fail16);
872fail15:
873	EFSYS_PROBE(fail15);
874fail14:
875	EFSYS_PROBE(fail14);
876fail13:
877	EFSYS_PROBE(fail13);
878fail12:
879	EFSYS_PROBE(fail12);
880fail11:
881	EFSYS_PROBE(fail11);
882fail10:
883	EFSYS_PROBE(fail10);
884fail9:
885	EFSYS_PROBE(fail9);
886fail8:
887	EFSYS_PROBE(fail8);
888fail7:
889	EFSYS_PROBE(fail7);
890fail6:
891	EFSYS_PROBE(fail6);
892fail5:
893	EFSYS_PROBE(fail5);
894fail4:
895	EFSYS_PROBE(fail4);
896fail3:
897	EFSYS_PROBE(fail3);
898fail2:
899	EFSYS_PROBE(fail2);
900fail1:
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