1 /* SPDX-License-Identifier: BSD-3-Clause
3 * Copyright(c) 2019-2020 Xilinx, Inc.
4 * Copyright(c) 2017-2019 Solarflare Communications Inc.
10 #include "ef10_firmware_ids.h"
12 #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
14 #if EFSYS_OPT_IMAGE_LAYOUT
17 * Utility routines to support limited parsing of ASN.1 tags. This is not a
18 * general purpose ASN.1 parser, but is sufficient to locate the required
19 * objects in a signed image with CMS headers.
22 /* DER encodings for ASN.1 tags (see ITU-T X.690) */
23 #define ASN1_TAG_INTEGER (0x02)
24 #define ASN1_TAG_OCTET_STRING (0x04)
25 #define ASN1_TAG_OBJ_ID (0x06)
26 #define ASN1_TAG_SEQUENCE (0x30)
27 #define ASN1_TAG_SET (0x31)
29 #define ASN1_TAG_IS_PRIM(tag) ((tag & 0x20) == 0)
31 #define ASN1_TAG_PRIM_CONTEXT(n) (0x80 + (n))
32 #define ASN1_TAG_CONS_CONTEXT(n) (0xA0 + (n))
34 typedef struct efx_asn1_cursor_s {
44 /* Parse header of DER encoded ASN.1 TLV and match tag */
45 static __checkReturn efx_rc_t
46 efx_asn1_parse_header_match_tag(
47 __inout efx_asn1_cursor_t *cursor,
52 if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) {
57 cursor->tag = cursor->buffer[0];
58 if (cursor->tag != tag) {
64 if ((cursor->tag & 0x1F) == 0x1F) {
65 /* Long tag format not used in CMS syntax */
70 if ((cursor->buffer[1] & 0x80) == 0) {
71 /* Short form: length is 0..127 */
73 cursor->val_size = cursor->buffer[1];
75 /* Long form: length encoded as [0x80+nbytes][length bytes] */
76 uint32_t nbytes = cursor->buffer[1] & 0x7F;
80 /* Indefinite length not allowed in DER encoding */
84 if (2 + nbytes > cursor->length) {
85 /* Header length overflows image buffer */
89 if (nbytes > sizeof (uint32_t)) {
90 /* Length encoding too big */
94 cursor->hdr_size = 2 + nbytes;
96 for (offset = 2; offset < cursor->hdr_size; offset++) {
98 (cursor->val_size << 8) | cursor->buffer[offset];
102 if ((cursor->hdr_size + cursor->val_size) > cursor->length) {
103 /* Length overflows image buffer */
123 EFSYS_PROBE1(fail1, efx_rc_t, rc);
128 /* Enter nested ASN.1 TLV (contained in value of current TLV) */
129 static __checkReturn efx_rc_t
131 __inout efx_asn1_cursor_t *cursor,
136 if (cursor == NULL) {
141 if (ASN1_TAG_IS_PRIM(tag)) {
142 /* Cannot enter a primitive tag */
146 rc = efx_asn1_parse_header_match_tag(cursor, tag);
148 /* Invalid TLV or wrong tag */
152 /* Limit cursor range to nested TLV */
153 cursor->buffer += cursor->hdr_size;
154 cursor->length = cursor->val_size;
163 EFSYS_PROBE1(fail1, efx_rc_t, rc);
169 * Check that the current ASN.1 TLV matches the given tag and value.
170 * Advance cursor to next TLV on a successful match.
172 static __checkReturn efx_rc_t
173 efx_asn1_match_tag_value(
174 __inout efx_asn1_cursor_t *cursor,
176 __in const void *valp,
177 __in uint32_t val_size)
181 if (cursor == NULL) {
185 rc = efx_asn1_parse_header_match_tag(cursor, tag);
187 /* Invalid TLV or wrong tag */
190 if (cursor->val_size != val_size) {
191 /* Value size is different */
195 if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) {
196 /* Value content is different */
200 cursor->buffer += cursor->hdr_size + cursor->val_size;
201 cursor->length -= cursor->hdr_size + cursor->val_size;
212 EFSYS_PROBE1(fail1, efx_rc_t, rc);
217 /* Advance cursor to next TLV */
218 static __checkReturn efx_rc_t
220 __inout efx_asn1_cursor_t *cursor,
225 if (cursor == NULL) {
230 rc = efx_asn1_parse_header_match_tag(cursor, tag);
232 /* Invalid TLV or wrong tag */
235 cursor->buffer += cursor->hdr_size + cursor->val_size;
236 cursor->length -= cursor->hdr_size + cursor->val_size;
243 EFSYS_PROBE1(fail1, efx_rc_t, rc);
248 /* Return pointer to value octets and value size from current TLV */
249 static __checkReturn efx_rc_t
250 efx_asn1_get_tag_value(
251 __inout efx_asn1_cursor_t *cursor,
253 __out uint8_t **valp,
254 __out uint32_t *val_sizep)
258 if (cursor == NULL || valp == NULL || val_sizep == NULL) {
263 rc = efx_asn1_parse_header_match_tag(cursor, tag);
265 /* Invalid TLV or wrong tag */
268 *valp = cursor->buffer + cursor->hdr_size;
269 *val_sizep = cursor->val_size;
276 EFSYS_PROBE1(fail1, efx_rc_t, rc);
283 * Utility routines for parsing CMS headers (see RFC2315, PKCS#7)
286 /* OID 1.2.840.113549.1.7.2 */
287 static const uint8_t PKCS7_SignedData[] =
288 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
290 /* OID 1.2.840.113549.1.7.1 */
291 static const uint8_t PKCS7_Data[] =
292 { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
294 /* SignedData structure version */
295 static const uint8_t SignedData_Version[] =
299 * Check for a valid image in signed image format. This uses CMS syntax
300 * (see RFC2315, PKCS#7) to provide signatures, and certificates required
301 * to validate the signatures. The encapsulated content is in unsigned image
302 * format (reflash header, image code, trailer checksum).
304 static __checkReturn efx_rc_t
305 efx_check_signed_image_header(
307 __in uint32_t buffer_size,
308 __out uint32_t *content_offsetp,
309 __out uint32_t *content_lengthp)
311 efx_asn1_cursor_t cursor;
316 if (content_offsetp == NULL || content_lengthp == NULL) {
320 cursor.buffer = (uint8_t *)bufferp;
321 cursor.length = buffer_size;
324 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
328 /* ContextInfo.contentType */
329 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
330 PKCS7_SignedData, sizeof (PKCS7_SignedData));
334 /* ContextInfo.content */
335 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
340 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
344 /* SignedData.version */
345 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER,
346 SignedData_Version, sizeof (SignedData_Version));
350 /* SignedData.digestAlgorithms */
351 rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET);
355 /* SignedData.encapContentInfo */
356 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
360 /* SignedData.encapContentInfo.econtentType */
361 rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
362 PKCS7_Data, sizeof (PKCS7_Data));
366 /* SignedData.encapContentInfo.econtent */
367 rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
372 * The octet string contains the image header, image code bytes and
373 * image trailer CRC (same as unsigned image layout).
377 rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING,
382 if ((valp == NULL) || (val_size == 0)) {
386 if (valp < (uint8_t *)bufferp) {
390 if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) {
395 *content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp);
396 *content_lengthp = val_size;
427 EFSYS_PROBE1(fail1, efx_rc_t, rc);
432 static __checkReturn efx_rc_t
433 efx_check_unsigned_image(
435 __in uint32_t buffer_size,
436 __out efx_image_header_t **headerpp,
437 __out efx_image_trailer_t **trailerpp)
439 efx_image_header_t *headerp;
440 efx_image_trailer_t *trailerp;
444 EFX_STATIC_ASSERT(sizeof (*headerp) == EFX_IMAGE_HEADER_SIZE);
445 EFX_STATIC_ASSERT(sizeof (*trailerp) == EFX_IMAGE_TRAILER_SIZE);
447 /* Must have at least enough space for required image header fields */
448 if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) +
449 sizeof (headerp->eih_size))) {
453 headerp = (efx_image_header_t *)bufferp;
455 /* Buffer must have space for image header, code and image trailer. */
456 if (buffer_size < (headerp->eih_size + headerp->eih_code_size +
457 EFX_IMAGE_TRAILER_SIZE)) {
462 trailerp = (efx_image_trailer_t *)((uint8_t *)headerp +
463 headerp->eih_size + headerp->eih_code_size);
466 *trailerpp = trailerp;
468 if (headerp->eih_magic != EFX_IMAGE_HEADER_MAGIC) {
474 * Check image header version is same or higher than lowest required
477 if (headerp->eih_version < EFX_IMAGE_HEADER_VERSION) {
482 /* Check CRC from image buffer matches computed CRC. */
483 crc = efx_crc32_calculate(0, (uint8_t *)headerp,
484 (headerp->eih_size + headerp->eih_code_size));
486 if (trailerp->eit_crc != crc) {
502 EFSYS_PROBE1(fail1, efx_rc_t, rc);
507 __checkReturn efx_rc_t
508 efx_check_reflash_image(
510 __in uint32_t buffer_size,
511 __out efx_image_info_t *infop)
513 efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE;
514 uint32_t image_offset;
517 efx_image_header_t *headerp;
518 efx_image_trailer_t *trailerp;
521 EFSYS_ASSERT(infop != NULL);
526 memset(infop, 0, sizeof (*infop));
528 if (bufferp == NULL || buffer_size == 0) {
534 * Check if the buffer contains an image in signed format, and if so,
535 * locate the image header.
537 rc = efx_check_signed_image_header(bufferp, buffer_size,
538 &image_offset, &image_size);
541 * Buffer holds signed image format. Check that the encapsulated
542 * content contains an unsigned image format header.
544 format = EFX_IMAGE_FORMAT_SIGNED;
546 /* Check if the buffer holds image in unsigned image format */
547 format = EFX_IMAGE_FORMAT_UNSIGNED;
549 image_size = buffer_size;
551 if (image_offset + image_size > buffer_size) {
555 imagep = (uint8_t *)bufferp + image_offset;
557 /* Check image layout (image header, code, image trailer) */
558 rc = efx_check_unsigned_image(imagep, image_size, &headerp, &trailerp);
563 * Signed images are packages consumed directly by the firmware,
564 * with the exception of MC firmware, where the image must be
565 * rearranged for booting purposes.
567 if (format == EFX_IMAGE_FORMAT_SIGNED) {
568 if (headerp->eih_type != FIRMWARE_TYPE_MCFW)
569 format = EFX_IMAGE_FORMAT_SIGNED_PACKAGE;
572 /* Return image details */
573 infop->eii_format = format;
574 infop->eii_imagep = bufferp;
575 infop->eii_image_size = buffer_size;
576 infop->eii_headerp = (efx_image_header_t *)imagep;
586 infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
587 infop->eii_imagep = NULL;
588 infop->eii_image_size = 0;
591 EFSYS_PROBE1(fail1, efx_rc_t, rc);
596 __checkReturn efx_rc_t
597 efx_build_signed_image_write_buffer(
598 __out_bcount(buffer_size)
600 __in uint32_t buffer_size,
601 __in efx_image_info_t *infop,
602 __out efx_image_header_t **headerpp)
604 signed_image_chunk_hdr_t chunk_hdr;
609 } cms_header, image_header, code, image_trailer, signature;
612 EFSYS_ASSERT((infop != NULL) && (headerpp != NULL));
614 if ((bufferp == NULL) || (buffer_size == 0) ||
615 (infop == NULL) || (headerpp == NULL)) {
616 /* Invalid arguments */
620 if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) ||
621 (infop->eii_imagep == NULL) ||
622 (infop->eii_headerp == NULL) ||
623 ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) ||
624 (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) ||
625 ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) >
626 (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) {
627 /* Invalid image info */
632 /* Locate image chunks in original signed image */
633 cms_header.offset = 0;
635 (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep);
636 if ((cms_header.size > buffer_size) ||
637 (cms_header.offset > (buffer_size - cms_header.size))) {
642 image_header.offset = cms_header.offset + cms_header.size;
643 image_header.size = infop->eii_headerp->eih_size;
644 if ((image_header.size > buffer_size) ||
645 (image_header.offset > (buffer_size - image_header.size))) {
650 code.offset = image_header.offset + image_header.size;
651 code.size = infop->eii_headerp->eih_code_size;
652 if ((code.size > buffer_size) ||
653 (code.offset > (buffer_size - code.size))) {
658 image_trailer.offset = code.offset + code.size;
659 image_trailer.size = EFX_IMAGE_TRAILER_SIZE;
660 if ((image_trailer.size > buffer_size) ||
661 (image_trailer.offset > (buffer_size - image_trailer.size))) {
666 signature.offset = image_trailer.offset + image_trailer.size;
667 signature.size = (uint32_t)(infop->eii_image_size - signature.offset);
668 if ((signature.size > buffer_size) ||
669 (signature.offset > (buffer_size - signature.size))) {
674 EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size +
675 image_header.size + code.size + image_trailer.size +
680 * Build signed image partition, inserting chunk headers.
682 * Signed Image: Image in NVRAM partition:
684 * +-----------------+ +-----------------+
685 * | CMS header | | mcfw.update |<----+
686 * +-----------------+ | | |
687 * | reflash header | +-----------------+ |
688 * +-----------------+ | chunk header: |-->--|-+
689 * | mcfw.update | | REFLASH_TRAILER | | |
690 * | | +-----------------+ | |
691 * +-----------------+ +-->| CMS header | | |
692 * | reflash trailer | | +-----------------+ | |
693 * +-----------------+ | | chunk header: |->-+ | |
694 * | signature | | | REFLASH_HEADER | | | |
695 * +-----------------+ | +-----------------+ | | |
696 * | | reflash header |<--+ | |
697 * | +-----------------+ | |
698 * | | chunk header: |-->--+ |
700 * | +-----------------+ |
701 * | | reflash trailer |<------+
702 * | +-----------------+
703 * | | chunk header: |
704 * | | SIGNATURE |->-+
705 * | +-----------------+ |
706 * | | signature |<--+
707 * | +-----------------+
709 * | +-----------------+
710 * +-<-| chunk header: |
712 * +-----------------+
714 * Each chunk header gives the partition offset and length of the image
715 * chunk's data. The image chunk data is immediately followed by the
716 * chunk header for the next chunk.
718 * The data chunk for the firmware code must be at the start of the
719 * partition (needed for the bootloader). The first chunk header in the
720 * chain (for the CMS header) is stored at the end of the partition. The
721 * chain of chunk headers maintains the same logical order of image
722 * chunks as the original signed image file. This set of constraints
723 * results in the layout used for the data chunks and chunk headers.
726 memset(bufferp, 0xFF, buffer_size);
728 EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN);
729 memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN);
734 if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) {
738 hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN;
740 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
741 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
742 chunk_hdr.id = SIGNED_IMAGE_CHUNK_CMS_HEADER;
743 chunk_hdr.offset = code.size + SIGNED_IMAGE_CHUNK_HDR_LEN;
744 chunk_hdr.len = cms_header.size;
746 memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr));
748 if ((chunk_hdr.len > buffer_size) ||
749 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
753 memcpy(bufferp + chunk_hdr.offset,
754 infop->eii_imagep + cms_header.offset,
760 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
761 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
765 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
766 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
767 chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_HEADER;
768 chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
769 chunk_hdr.len = image_header.size;
771 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
773 if ((chunk_hdr.len > buffer_size) ||
774 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
778 memcpy(bufferp + chunk_hdr.offset,
779 infop->eii_imagep + image_header.offset,
782 *headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset);
787 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
788 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
792 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
793 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
794 chunk_hdr.id = SIGNED_IMAGE_CHUNK_IMAGE;
795 chunk_hdr.offset = 0;
796 chunk_hdr.len = code.size;
798 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
800 if ((chunk_hdr.len > buffer_size) ||
801 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
805 memcpy(bufferp + chunk_hdr.offset,
806 infop->eii_imagep + code.offset,
810 * Image trailer (CRC)
812 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
813 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
814 chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_TRAILER;
815 chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
816 chunk_hdr.len = image_trailer.size;
818 hdr_offset = code.size;
819 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
824 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
826 if ((chunk_hdr.len > buffer_size) ||
827 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
831 memcpy((uint8_t *)bufferp + chunk_hdr.offset,
832 infop->eii_imagep + image_trailer.offset,
838 hdr_offset = chunk_hdr.offset + chunk_hdr.len;
839 if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
843 chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
844 chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
845 chunk_hdr.id = SIGNED_IMAGE_CHUNK_SIGNATURE;
846 chunk_hdr.offset = chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
847 chunk_hdr.len = signature.size;
849 memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
851 if ((chunk_hdr.len > buffer_size) ||
852 (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
856 memcpy(bufferp + chunk_hdr.offset,
857 infop->eii_imagep + signature.offset,
895 EFSYS_PROBE1(fail1, efx_rc_t, rc);
902 #endif /* EFSYS_OPT_IMAGE_LAYOUT */
904 #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */