15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko *
3*672386c1SAndrew Rybchenko * Copyright(c) 2019-2021 Xilinx, Inc.
45e111ed8SAndrew Rybchenko * Copyright(c) 2017-2019 Solarflare Communications Inc.
55e111ed8SAndrew Rybchenko */
65e111ed8SAndrew Rybchenko
75e111ed8SAndrew Rybchenko #include "efx.h"
85e111ed8SAndrew Rybchenko #include "efx_impl.h"
95e111ed8SAndrew Rybchenko
105e111ed8SAndrew Rybchenko #include "ef10_firmware_ids.h"
115e111ed8SAndrew Rybchenko
125e111ed8SAndrew Rybchenko #if EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2
135e111ed8SAndrew Rybchenko
145e111ed8SAndrew Rybchenko #if EFSYS_OPT_IMAGE_LAYOUT
155e111ed8SAndrew Rybchenko
165e111ed8SAndrew Rybchenko /*
175e111ed8SAndrew Rybchenko * Utility routines to support limited parsing of ASN.1 tags. This is not a
185e111ed8SAndrew Rybchenko * general purpose ASN.1 parser, but is sufficient to locate the required
195e111ed8SAndrew Rybchenko * objects in a signed image with CMS headers.
205e111ed8SAndrew Rybchenko */
215e111ed8SAndrew Rybchenko
225e111ed8SAndrew Rybchenko /* DER encodings for ASN.1 tags (see ITU-T X.690) */
235e111ed8SAndrew Rybchenko #define ASN1_TAG_INTEGER (0x02)
245e111ed8SAndrew Rybchenko #define ASN1_TAG_OCTET_STRING (0x04)
255e111ed8SAndrew Rybchenko #define ASN1_TAG_OBJ_ID (0x06)
265e111ed8SAndrew Rybchenko #define ASN1_TAG_SEQUENCE (0x30)
275e111ed8SAndrew Rybchenko #define ASN1_TAG_SET (0x31)
285e111ed8SAndrew Rybchenko
295e111ed8SAndrew Rybchenko #define ASN1_TAG_IS_PRIM(tag) ((tag & 0x20) == 0)
305e111ed8SAndrew Rybchenko
315e111ed8SAndrew Rybchenko #define ASN1_TAG_PRIM_CONTEXT(n) (0x80 + (n))
325e111ed8SAndrew Rybchenko #define ASN1_TAG_CONS_CONTEXT(n) (0xA0 + (n))
335e111ed8SAndrew Rybchenko
345e111ed8SAndrew Rybchenko typedef struct efx_asn1_cursor_s {
355e111ed8SAndrew Rybchenko uint8_t *buffer;
365e111ed8SAndrew Rybchenko uint32_t length;
375e111ed8SAndrew Rybchenko
385e111ed8SAndrew Rybchenko uint8_t tag;
395e111ed8SAndrew Rybchenko uint32_t hdr_size;
405e111ed8SAndrew Rybchenko uint32_t val_size;
415e111ed8SAndrew Rybchenko } efx_asn1_cursor_t;
425e111ed8SAndrew Rybchenko
435e111ed8SAndrew Rybchenko
445e111ed8SAndrew Rybchenko /* Parse header of DER encoded ASN.1 TLV and match tag */
455e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_asn1_parse_header_match_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)465e111ed8SAndrew Rybchenko efx_asn1_parse_header_match_tag(
475e111ed8SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor,
485e111ed8SAndrew Rybchenko __in uint8_t tag)
495e111ed8SAndrew Rybchenko {
505e111ed8SAndrew Rybchenko efx_rc_t rc;
515e111ed8SAndrew Rybchenko
525e111ed8SAndrew Rybchenko if (cursor == NULL || cursor->buffer == NULL || cursor->length < 2) {
535e111ed8SAndrew Rybchenko rc = EINVAL;
545e111ed8SAndrew Rybchenko goto fail1;
555e111ed8SAndrew Rybchenko }
565e111ed8SAndrew Rybchenko
575e111ed8SAndrew Rybchenko cursor->tag = cursor->buffer[0];
585e111ed8SAndrew Rybchenko if (cursor->tag != tag) {
595e111ed8SAndrew Rybchenko /* Tag not matched */
605e111ed8SAndrew Rybchenko rc = ENOENT;
615e111ed8SAndrew Rybchenko goto fail2;
625e111ed8SAndrew Rybchenko }
635e111ed8SAndrew Rybchenko
645e111ed8SAndrew Rybchenko if ((cursor->tag & 0x1F) == 0x1F) {
655e111ed8SAndrew Rybchenko /* Long tag format not used in CMS syntax */
665e111ed8SAndrew Rybchenko rc = EINVAL;
675e111ed8SAndrew Rybchenko goto fail3;
685e111ed8SAndrew Rybchenko }
695e111ed8SAndrew Rybchenko
705e111ed8SAndrew Rybchenko if ((cursor->buffer[1] & 0x80) == 0) {
715e111ed8SAndrew Rybchenko /* Short form: length is 0..127 */
725e111ed8SAndrew Rybchenko cursor->hdr_size = 2;
735e111ed8SAndrew Rybchenko cursor->val_size = cursor->buffer[1];
745e111ed8SAndrew Rybchenko } else {
755e111ed8SAndrew Rybchenko /* Long form: length encoded as [0x80+nbytes][length bytes] */
765e111ed8SAndrew Rybchenko uint32_t nbytes = cursor->buffer[1] & 0x7F;
775e111ed8SAndrew Rybchenko uint32_t offset;
785e111ed8SAndrew Rybchenko
795e111ed8SAndrew Rybchenko if (nbytes == 0) {
805e111ed8SAndrew Rybchenko /* Indefinite length not allowed in DER encoding */
815e111ed8SAndrew Rybchenko rc = EINVAL;
825e111ed8SAndrew Rybchenko goto fail4;
835e111ed8SAndrew Rybchenko }
845e111ed8SAndrew Rybchenko if (2 + nbytes > cursor->length) {
855e111ed8SAndrew Rybchenko /* Header length overflows image buffer */
865e111ed8SAndrew Rybchenko rc = EINVAL;
875e111ed8SAndrew Rybchenko goto fail6;
885e111ed8SAndrew Rybchenko }
895e111ed8SAndrew Rybchenko if (nbytes > sizeof (uint32_t)) {
905e111ed8SAndrew Rybchenko /* Length encoding too big */
915e111ed8SAndrew Rybchenko rc = E2BIG;
925e111ed8SAndrew Rybchenko goto fail5;
935e111ed8SAndrew Rybchenko }
945e111ed8SAndrew Rybchenko cursor->hdr_size = 2 + nbytes;
955e111ed8SAndrew Rybchenko cursor->val_size = 0;
965e111ed8SAndrew Rybchenko for (offset = 2; offset < cursor->hdr_size; offset++) {
975e111ed8SAndrew Rybchenko cursor->val_size =
985e111ed8SAndrew Rybchenko (cursor->val_size << 8) | cursor->buffer[offset];
995e111ed8SAndrew Rybchenko }
1005e111ed8SAndrew Rybchenko }
1015e111ed8SAndrew Rybchenko
1025e111ed8SAndrew Rybchenko if ((cursor->hdr_size + cursor->val_size) > cursor->length) {
1035e111ed8SAndrew Rybchenko /* Length overflows image buffer */
1045e111ed8SAndrew Rybchenko rc = E2BIG;
1055e111ed8SAndrew Rybchenko goto fail7;
1065e111ed8SAndrew Rybchenko }
1075e111ed8SAndrew Rybchenko
1085e111ed8SAndrew Rybchenko return (0);
1095e111ed8SAndrew Rybchenko
1105e111ed8SAndrew Rybchenko fail7:
1115e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
1125e111ed8SAndrew Rybchenko fail6:
1135e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
1145e111ed8SAndrew Rybchenko fail5:
1155e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
1165e111ed8SAndrew Rybchenko fail4:
1175e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
1185e111ed8SAndrew Rybchenko fail3:
1195e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
1205e111ed8SAndrew Rybchenko fail2:
1215e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
1225e111ed8SAndrew Rybchenko fail1:
1235e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
1245e111ed8SAndrew Rybchenko
1255e111ed8SAndrew Rybchenko return (rc);
1265e111ed8SAndrew Rybchenko }
1275e111ed8SAndrew Rybchenko
1285e111ed8SAndrew Rybchenko /* Enter nested ASN.1 TLV (contained in value of current TLV) */
1295e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_asn1_enter_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)1305e111ed8SAndrew Rybchenko efx_asn1_enter_tag(
1315e111ed8SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor,
1325e111ed8SAndrew Rybchenko __in uint8_t tag)
1335e111ed8SAndrew Rybchenko {
1345e111ed8SAndrew Rybchenko efx_rc_t rc;
1355e111ed8SAndrew Rybchenko
1365e111ed8SAndrew Rybchenko if (cursor == NULL) {
1375e111ed8SAndrew Rybchenko rc = EINVAL;
1385e111ed8SAndrew Rybchenko goto fail1;
1395e111ed8SAndrew Rybchenko }
1405e111ed8SAndrew Rybchenko
1415e111ed8SAndrew Rybchenko if (ASN1_TAG_IS_PRIM(tag)) {
1425e111ed8SAndrew Rybchenko /* Cannot enter a primitive tag */
1435e111ed8SAndrew Rybchenko rc = ENOTSUP;
1445e111ed8SAndrew Rybchenko goto fail2;
1455e111ed8SAndrew Rybchenko }
1465e111ed8SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag);
1475e111ed8SAndrew Rybchenko if (rc != 0) {
1485e111ed8SAndrew Rybchenko /* Invalid TLV or wrong tag */
1495e111ed8SAndrew Rybchenko goto fail3;
1505e111ed8SAndrew Rybchenko }
1515e111ed8SAndrew Rybchenko
1525e111ed8SAndrew Rybchenko /* Limit cursor range to nested TLV */
1535e111ed8SAndrew Rybchenko cursor->buffer += cursor->hdr_size;
1545e111ed8SAndrew Rybchenko cursor->length = cursor->val_size;
1555e111ed8SAndrew Rybchenko
1565e111ed8SAndrew Rybchenko return (0);
1575e111ed8SAndrew Rybchenko
1585e111ed8SAndrew Rybchenko fail3:
1595e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
1605e111ed8SAndrew Rybchenko fail2:
1615e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
1625e111ed8SAndrew Rybchenko fail1:
1635e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
1645e111ed8SAndrew Rybchenko
1655e111ed8SAndrew Rybchenko return (rc);
1665e111ed8SAndrew Rybchenko }
1675e111ed8SAndrew Rybchenko
1685e111ed8SAndrew Rybchenko /*
1695e111ed8SAndrew Rybchenko * Check that the current ASN.1 TLV matches the given tag and value.
1705e111ed8SAndrew Rybchenko * Advance cursor to next TLV on a successful match.
1715e111ed8SAndrew Rybchenko */
1725e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_asn1_match_tag_value(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag,__in const void * valp,__in uint32_t val_size)1735e111ed8SAndrew Rybchenko efx_asn1_match_tag_value(
1745e111ed8SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor,
1755e111ed8SAndrew Rybchenko __in uint8_t tag,
1765e111ed8SAndrew Rybchenko __in const void *valp,
1775e111ed8SAndrew Rybchenko __in uint32_t val_size)
1785e111ed8SAndrew Rybchenko {
1795e111ed8SAndrew Rybchenko efx_rc_t rc;
1805e111ed8SAndrew Rybchenko
1815e111ed8SAndrew Rybchenko if (cursor == NULL) {
1825e111ed8SAndrew Rybchenko rc = EINVAL;
1835e111ed8SAndrew Rybchenko goto fail1;
1845e111ed8SAndrew Rybchenko }
1855e111ed8SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag);
1865e111ed8SAndrew Rybchenko if (rc != 0) {
1875e111ed8SAndrew Rybchenko /* Invalid TLV or wrong tag */
1885e111ed8SAndrew Rybchenko goto fail2;
1895e111ed8SAndrew Rybchenko }
1905e111ed8SAndrew Rybchenko if (cursor->val_size != val_size) {
1915e111ed8SAndrew Rybchenko /* Value size is different */
1925e111ed8SAndrew Rybchenko rc = EINVAL;
1935e111ed8SAndrew Rybchenko goto fail3;
1945e111ed8SAndrew Rybchenko }
1955e111ed8SAndrew Rybchenko if (memcmp(cursor->buffer + cursor->hdr_size, valp, val_size) != 0) {
1965e111ed8SAndrew Rybchenko /* Value content is different */
1975e111ed8SAndrew Rybchenko rc = EINVAL;
1985e111ed8SAndrew Rybchenko goto fail4;
1995e111ed8SAndrew Rybchenko }
2005e111ed8SAndrew Rybchenko cursor->buffer += cursor->hdr_size + cursor->val_size;
2015e111ed8SAndrew Rybchenko cursor->length -= cursor->hdr_size + cursor->val_size;
2025e111ed8SAndrew Rybchenko
2035e111ed8SAndrew Rybchenko return (0);
2045e111ed8SAndrew Rybchenko
2055e111ed8SAndrew Rybchenko fail4:
2065e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
2075e111ed8SAndrew Rybchenko fail3:
2085e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
2095e111ed8SAndrew Rybchenko fail2:
2105e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
2115e111ed8SAndrew Rybchenko fail1:
2125e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2135e111ed8SAndrew Rybchenko
2145e111ed8SAndrew Rybchenko return (rc);
2155e111ed8SAndrew Rybchenko }
2165e111ed8SAndrew Rybchenko
2175e111ed8SAndrew Rybchenko /* Advance cursor to next TLV */
2185e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_asn1_skip_tag(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag)2195e111ed8SAndrew Rybchenko efx_asn1_skip_tag(
2205e111ed8SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor,
2215e111ed8SAndrew Rybchenko __in uint8_t tag)
2225e111ed8SAndrew Rybchenko {
2235e111ed8SAndrew Rybchenko efx_rc_t rc;
2245e111ed8SAndrew Rybchenko
2255e111ed8SAndrew Rybchenko if (cursor == NULL) {
2265e111ed8SAndrew Rybchenko rc = EINVAL;
2275e111ed8SAndrew Rybchenko goto fail1;
2285e111ed8SAndrew Rybchenko }
2295e111ed8SAndrew Rybchenko
2305e111ed8SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag);
2315e111ed8SAndrew Rybchenko if (rc != 0) {
2325e111ed8SAndrew Rybchenko /* Invalid TLV or wrong tag */
2335e111ed8SAndrew Rybchenko goto fail2;
2345e111ed8SAndrew Rybchenko }
2355e111ed8SAndrew Rybchenko cursor->buffer += cursor->hdr_size + cursor->val_size;
2365e111ed8SAndrew Rybchenko cursor->length -= cursor->hdr_size + cursor->val_size;
2375e111ed8SAndrew Rybchenko
2385e111ed8SAndrew Rybchenko return (0);
2395e111ed8SAndrew Rybchenko
2405e111ed8SAndrew Rybchenko fail2:
2415e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
2425e111ed8SAndrew Rybchenko fail1:
2435e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2445e111ed8SAndrew Rybchenko
2455e111ed8SAndrew Rybchenko return (rc);
2465e111ed8SAndrew Rybchenko }
2475e111ed8SAndrew Rybchenko
2485e111ed8SAndrew Rybchenko /* Return pointer to value octets and value size from current TLV */
2495e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_asn1_get_tag_value(__inout efx_asn1_cursor_t * cursor,__in uint8_t tag,__out uint8_t ** valp,__out uint32_t * val_sizep)2505e111ed8SAndrew Rybchenko efx_asn1_get_tag_value(
2515e111ed8SAndrew Rybchenko __inout efx_asn1_cursor_t *cursor,
2525e111ed8SAndrew Rybchenko __in uint8_t tag,
2535e111ed8SAndrew Rybchenko __out uint8_t **valp,
2545e111ed8SAndrew Rybchenko __out uint32_t *val_sizep)
2555e111ed8SAndrew Rybchenko {
2565e111ed8SAndrew Rybchenko efx_rc_t rc;
2575e111ed8SAndrew Rybchenko
2585e111ed8SAndrew Rybchenko if (cursor == NULL || valp == NULL || val_sizep == NULL) {
2595e111ed8SAndrew Rybchenko rc = EINVAL;
2605e111ed8SAndrew Rybchenko goto fail1;
2615e111ed8SAndrew Rybchenko }
2625e111ed8SAndrew Rybchenko
2635e111ed8SAndrew Rybchenko rc = efx_asn1_parse_header_match_tag(cursor, tag);
2645e111ed8SAndrew Rybchenko if (rc != 0) {
2655e111ed8SAndrew Rybchenko /* Invalid TLV or wrong tag */
2665e111ed8SAndrew Rybchenko goto fail2;
2675e111ed8SAndrew Rybchenko }
2685e111ed8SAndrew Rybchenko *valp = cursor->buffer + cursor->hdr_size;
2695e111ed8SAndrew Rybchenko *val_sizep = cursor->val_size;
2705e111ed8SAndrew Rybchenko
2715e111ed8SAndrew Rybchenko return (0);
2725e111ed8SAndrew Rybchenko
2735e111ed8SAndrew Rybchenko fail2:
2745e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
2755e111ed8SAndrew Rybchenko fail1:
2765e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2775e111ed8SAndrew Rybchenko
2785e111ed8SAndrew Rybchenko return (rc);
2795e111ed8SAndrew Rybchenko }
2805e111ed8SAndrew Rybchenko
2815e111ed8SAndrew Rybchenko
2825e111ed8SAndrew Rybchenko /*
2835e111ed8SAndrew Rybchenko * Utility routines for parsing CMS headers (see RFC2315, PKCS#7)
2845e111ed8SAndrew Rybchenko */
2855e111ed8SAndrew Rybchenko
2865e111ed8SAndrew Rybchenko /* OID 1.2.840.113549.1.7.2 */
2875e111ed8SAndrew Rybchenko static const uint8_t PKCS7_SignedData[] =
2885e111ed8SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02 };
2895e111ed8SAndrew Rybchenko
2905e111ed8SAndrew Rybchenko /* OID 1.2.840.113549.1.7.1 */
2915e111ed8SAndrew Rybchenko static const uint8_t PKCS7_Data[] =
2925e111ed8SAndrew Rybchenko { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 };
2935e111ed8SAndrew Rybchenko
2945e111ed8SAndrew Rybchenko /* SignedData structure version */
2955e111ed8SAndrew Rybchenko static const uint8_t SignedData_Version[] =
2965e111ed8SAndrew Rybchenko { 0x03 };
2975e111ed8SAndrew Rybchenko
2985e111ed8SAndrew Rybchenko /*
2995e111ed8SAndrew Rybchenko * Check for a valid image in signed image format. This uses CMS syntax
3005e111ed8SAndrew Rybchenko * (see RFC2315, PKCS#7) to provide signatures, and certificates required
3015e111ed8SAndrew Rybchenko * to validate the signatures. The encapsulated content is in unsigned image
3025e111ed8SAndrew Rybchenko * format (reflash header, image code, trailer checksum).
3035e111ed8SAndrew Rybchenko */
3045e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_check_signed_image_header(__in void * bufferp,__in uint32_t buffer_size,__out uint32_t * content_offsetp,__out uint32_t * content_lengthp)3055e111ed8SAndrew Rybchenko efx_check_signed_image_header(
3065e111ed8SAndrew Rybchenko __in void *bufferp,
3075e111ed8SAndrew Rybchenko __in uint32_t buffer_size,
3085e111ed8SAndrew Rybchenko __out uint32_t *content_offsetp,
3095e111ed8SAndrew Rybchenko __out uint32_t *content_lengthp)
3105e111ed8SAndrew Rybchenko {
3115e111ed8SAndrew Rybchenko efx_asn1_cursor_t cursor;
3125e111ed8SAndrew Rybchenko uint8_t *valp;
3135e111ed8SAndrew Rybchenko uint32_t val_size;
3145e111ed8SAndrew Rybchenko efx_rc_t rc;
3155e111ed8SAndrew Rybchenko
3165e111ed8SAndrew Rybchenko if (content_offsetp == NULL || content_lengthp == NULL) {
3175e111ed8SAndrew Rybchenko rc = EINVAL;
3185e111ed8SAndrew Rybchenko goto fail1;
3195e111ed8SAndrew Rybchenko }
3205e111ed8SAndrew Rybchenko cursor.buffer = (uint8_t *)bufferp;
3215e111ed8SAndrew Rybchenko cursor.length = buffer_size;
3225e111ed8SAndrew Rybchenko
3235e111ed8SAndrew Rybchenko /* ContextInfo */
3245e111ed8SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
3255e111ed8SAndrew Rybchenko if (rc != 0)
3265e111ed8SAndrew Rybchenko goto fail2;
3275e111ed8SAndrew Rybchenko
3285e111ed8SAndrew Rybchenko /* ContextInfo.contentType */
3295e111ed8SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
3305e111ed8SAndrew Rybchenko PKCS7_SignedData, sizeof (PKCS7_SignedData));
3315e111ed8SAndrew Rybchenko if (rc != 0)
3325e111ed8SAndrew Rybchenko goto fail3;
3335e111ed8SAndrew Rybchenko
3345e111ed8SAndrew Rybchenko /* ContextInfo.content */
3355e111ed8SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
3365e111ed8SAndrew Rybchenko if (rc != 0)
3375e111ed8SAndrew Rybchenko goto fail4;
3385e111ed8SAndrew Rybchenko
3395e111ed8SAndrew Rybchenko /* SignedData */
3405e111ed8SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
3415e111ed8SAndrew Rybchenko if (rc != 0)
3425e111ed8SAndrew Rybchenko goto fail5;
3435e111ed8SAndrew Rybchenko
3445e111ed8SAndrew Rybchenko /* SignedData.version */
3455e111ed8SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_INTEGER,
3465e111ed8SAndrew Rybchenko SignedData_Version, sizeof (SignedData_Version));
3475e111ed8SAndrew Rybchenko if (rc != 0)
3485e111ed8SAndrew Rybchenko goto fail6;
3495e111ed8SAndrew Rybchenko
3505e111ed8SAndrew Rybchenko /* SignedData.digestAlgorithms */
3515e111ed8SAndrew Rybchenko rc = efx_asn1_skip_tag(&cursor, ASN1_TAG_SET);
3525e111ed8SAndrew Rybchenko if (rc != 0)
3535e111ed8SAndrew Rybchenko goto fail7;
3545e111ed8SAndrew Rybchenko
3555e111ed8SAndrew Rybchenko /* SignedData.encapContentInfo */
3565e111ed8SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_SEQUENCE);
3575e111ed8SAndrew Rybchenko if (rc != 0)
3585e111ed8SAndrew Rybchenko goto fail8;
3595e111ed8SAndrew Rybchenko
3605e111ed8SAndrew Rybchenko /* SignedData.encapContentInfo.econtentType */
3615e111ed8SAndrew Rybchenko rc = efx_asn1_match_tag_value(&cursor, ASN1_TAG_OBJ_ID,
3625e111ed8SAndrew Rybchenko PKCS7_Data, sizeof (PKCS7_Data));
3635e111ed8SAndrew Rybchenko if (rc != 0)
3645e111ed8SAndrew Rybchenko goto fail9;
3655e111ed8SAndrew Rybchenko
3665e111ed8SAndrew Rybchenko /* SignedData.encapContentInfo.econtent */
3675e111ed8SAndrew Rybchenko rc = efx_asn1_enter_tag(&cursor, ASN1_TAG_CONS_CONTEXT(0));
3685e111ed8SAndrew Rybchenko if (rc != 0)
3695e111ed8SAndrew Rybchenko goto fail10;
3705e111ed8SAndrew Rybchenko
3715e111ed8SAndrew Rybchenko /*
3725e111ed8SAndrew Rybchenko * The octet string contains the image header, image code bytes and
3735e111ed8SAndrew Rybchenko * image trailer CRC (same as unsigned image layout).
3745e111ed8SAndrew Rybchenko */
3755e111ed8SAndrew Rybchenko valp = NULL;
3765e111ed8SAndrew Rybchenko val_size = 0;
3775e111ed8SAndrew Rybchenko rc = efx_asn1_get_tag_value(&cursor, ASN1_TAG_OCTET_STRING,
3785e111ed8SAndrew Rybchenko &valp, &val_size);
3795e111ed8SAndrew Rybchenko if (rc != 0)
3805e111ed8SAndrew Rybchenko goto fail11;
3815e111ed8SAndrew Rybchenko
3825e111ed8SAndrew Rybchenko if ((valp == NULL) || (val_size == 0)) {
3835e111ed8SAndrew Rybchenko rc = EINVAL;
3845e111ed8SAndrew Rybchenko goto fail12;
3855e111ed8SAndrew Rybchenko }
3865e111ed8SAndrew Rybchenko if (valp < (uint8_t *)bufferp) {
3875e111ed8SAndrew Rybchenko rc = EINVAL;
3885e111ed8SAndrew Rybchenko goto fail13;
3895e111ed8SAndrew Rybchenko }
3905e111ed8SAndrew Rybchenko if ((valp + val_size) > ((uint8_t *)bufferp + buffer_size)) {
3915e111ed8SAndrew Rybchenko rc = EINVAL;
3925e111ed8SAndrew Rybchenko goto fail14;
3935e111ed8SAndrew Rybchenko }
3945e111ed8SAndrew Rybchenko
3955e111ed8SAndrew Rybchenko *content_offsetp = (uint32_t)(valp - (uint8_t *)bufferp);
3965e111ed8SAndrew Rybchenko *content_lengthp = val_size;
3975e111ed8SAndrew Rybchenko
3985e111ed8SAndrew Rybchenko return (0);
3995e111ed8SAndrew Rybchenko
4005e111ed8SAndrew Rybchenko fail14:
4015e111ed8SAndrew Rybchenko EFSYS_PROBE(fail14);
4025e111ed8SAndrew Rybchenko fail13:
4035e111ed8SAndrew Rybchenko EFSYS_PROBE(fail13);
4045e111ed8SAndrew Rybchenko fail12:
4055e111ed8SAndrew Rybchenko EFSYS_PROBE(fail12);
4065e111ed8SAndrew Rybchenko fail11:
4075e111ed8SAndrew Rybchenko EFSYS_PROBE(fail11);
4085e111ed8SAndrew Rybchenko fail10:
4095e111ed8SAndrew Rybchenko EFSYS_PROBE(fail10);
4105e111ed8SAndrew Rybchenko fail9:
4115e111ed8SAndrew Rybchenko EFSYS_PROBE(fail9);
4125e111ed8SAndrew Rybchenko fail8:
4135e111ed8SAndrew Rybchenko EFSYS_PROBE(fail8);
4145e111ed8SAndrew Rybchenko fail7:
4155e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
4165e111ed8SAndrew Rybchenko fail6:
4175e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
4185e111ed8SAndrew Rybchenko fail5:
4195e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
4205e111ed8SAndrew Rybchenko fail4:
4215e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
4225e111ed8SAndrew Rybchenko fail3:
4235e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
4245e111ed8SAndrew Rybchenko fail2:
4255e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
4265e111ed8SAndrew Rybchenko fail1:
4275e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
4285e111ed8SAndrew Rybchenko
4295e111ed8SAndrew Rybchenko return (rc);
4305e111ed8SAndrew Rybchenko }
4315e111ed8SAndrew Rybchenko
4325e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
efx_check_unsigned_image(__in void * bufferp,__in uint32_t buffer_size,__out efx_image_header_t ** headerpp,__out efx_image_trailer_t ** trailerpp)4335e111ed8SAndrew Rybchenko efx_check_unsigned_image(
4345e111ed8SAndrew Rybchenko __in void *bufferp,
4355e111ed8SAndrew Rybchenko __in uint32_t buffer_size,
4365e111ed8SAndrew Rybchenko __out efx_image_header_t **headerpp,
4375e111ed8SAndrew Rybchenko __out efx_image_trailer_t **trailerpp)
4385e111ed8SAndrew Rybchenko {
4395e111ed8SAndrew Rybchenko efx_image_header_t *headerp;
4405e111ed8SAndrew Rybchenko efx_image_trailer_t *trailerp;
4415e111ed8SAndrew Rybchenko uint32_t crc;
4425e111ed8SAndrew Rybchenko efx_rc_t rc;
4435e111ed8SAndrew Rybchenko
4445e111ed8SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*headerp) == EFX_IMAGE_HEADER_SIZE);
4455e111ed8SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*trailerp) == EFX_IMAGE_TRAILER_SIZE);
4465e111ed8SAndrew Rybchenko
4475e111ed8SAndrew Rybchenko /* Must have at least enough space for required image header fields */
4485e111ed8SAndrew Rybchenko if (buffer_size < (EFX_FIELD_OFFSET(efx_image_header_t, eih_size) +
4495e111ed8SAndrew Rybchenko sizeof (headerp->eih_size))) {
4505e111ed8SAndrew Rybchenko rc = ENOSPC;
4515e111ed8SAndrew Rybchenko goto fail1;
4525e111ed8SAndrew Rybchenko }
4535e111ed8SAndrew Rybchenko headerp = (efx_image_header_t *)bufferp;
4545e111ed8SAndrew Rybchenko
4555e111ed8SAndrew Rybchenko /* Buffer must have space for image header, code and image trailer. */
4565e111ed8SAndrew Rybchenko if (buffer_size < (headerp->eih_size + headerp->eih_code_size +
4575e111ed8SAndrew Rybchenko EFX_IMAGE_TRAILER_SIZE)) {
4585e111ed8SAndrew Rybchenko rc = ENOSPC;
4595e111ed8SAndrew Rybchenko goto fail2;
4605e111ed8SAndrew Rybchenko }
4615e111ed8SAndrew Rybchenko
4625e111ed8SAndrew Rybchenko trailerp = (efx_image_trailer_t *)((uint8_t *)headerp +
4635e111ed8SAndrew Rybchenko headerp->eih_size + headerp->eih_code_size);
4645e111ed8SAndrew Rybchenko
4655e111ed8SAndrew Rybchenko *headerpp = headerp;
4665e111ed8SAndrew Rybchenko *trailerpp = trailerp;
4675e111ed8SAndrew Rybchenko
4685e111ed8SAndrew Rybchenko if (headerp->eih_magic != EFX_IMAGE_HEADER_MAGIC) {
4695e111ed8SAndrew Rybchenko rc = EINVAL;
4705e111ed8SAndrew Rybchenko goto fail3;
4715e111ed8SAndrew Rybchenko }
4725e111ed8SAndrew Rybchenko
4735e111ed8SAndrew Rybchenko /*
4745e111ed8SAndrew Rybchenko * Check image header version is same or higher than lowest required
4755e111ed8SAndrew Rybchenko * version.
4765e111ed8SAndrew Rybchenko */
4775e111ed8SAndrew Rybchenko if (headerp->eih_version < EFX_IMAGE_HEADER_VERSION) {
4785e111ed8SAndrew Rybchenko rc = EINVAL;
4795e111ed8SAndrew Rybchenko goto fail4;
4805e111ed8SAndrew Rybchenko }
4815e111ed8SAndrew Rybchenko
4825e111ed8SAndrew Rybchenko /* Check CRC from image buffer matches computed CRC. */
4835e111ed8SAndrew Rybchenko crc = efx_crc32_calculate(0, (uint8_t *)headerp,
4845e111ed8SAndrew Rybchenko (headerp->eih_size + headerp->eih_code_size));
4855e111ed8SAndrew Rybchenko
4865e111ed8SAndrew Rybchenko if (trailerp->eit_crc != crc) {
4875e111ed8SAndrew Rybchenko rc = EINVAL;
4885e111ed8SAndrew Rybchenko goto fail5;
4895e111ed8SAndrew Rybchenko }
4905e111ed8SAndrew Rybchenko
4915e111ed8SAndrew Rybchenko return (0);
4925e111ed8SAndrew Rybchenko
4935e111ed8SAndrew Rybchenko fail5:
4945e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
4955e111ed8SAndrew Rybchenko fail4:
4965e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
4975e111ed8SAndrew Rybchenko fail3:
4985e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
4995e111ed8SAndrew Rybchenko fail2:
5005e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
5015e111ed8SAndrew Rybchenko fail1:
5025e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
5035e111ed8SAndrew Rybchenko
5045e111ed8SAndrew Rybchenko return (rc);
5055e111ed8SAndrew Rybchenko }
5065e111ed8SAndrew Rybchenko
5075e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
efx_check_reflash_image(__in void * bufferp,__in uint32_t buffer_size,__out efx_image_info_t * infop)5085e111ed8SAndrew Rybchenko efx_check_reflash_image(
5095e111ed8SAndrew Rybchenko __in void *bufferp,
5105e111ed8SAndrew Rybchenko __in uint32_t buffer_size,
5115e111ed8SAndrew Rybchenko __out efx_image_info_t *infop)
5125e111ed8SAndrew Rybchenko {
5135e111ed8SAndrew Rybchenko efx_image_format_t format = EFX_IMAGE_FORMAT_NO_IMAGE;
5145e111ed8SAndrew Rybchenko uint32_t image_offset;
5155e111ed8SAndrew Rybchenko uint32_t image_size;
5165e111ed8SAndrew Rybchenko void *imagep;
5175e111ed8SAndrew Rybchenko efx_image_header_t *headerp;
5185e111ed8SAndrew Rybchenko efx_image_trailer_t *trailerp;
5195e111ed8SAndrew Rybchenko efx_rc_t rc;
5205e111ed8SAndrew Rybchenko
5215e111ed8SAndrew Rybchenko EFSYS_ASSERT(infop != NULL);
5225e111ed8SAndrew Rybchenko if (infop == NULL) {
5235e111ed8SAndrew Rybchenko rc = EINVAL;
5245e111ed8SAndrew Rybchenko goto fail1;
5255e111ed8SAndrew Rybchenko }
5265e111ed8SAndrew Rybchenko memset(infop, 0, sizeof (*infop));
5275e111ed8SAndrew Rybchenko
5285e111ed8SAndrew Rybchenko if (bufferp == NULL || buffer_size == 0) {
5295e111ed8SAndrew Rybchenko rc = EINVAL;
5305e111ed8SAndrew Rybchenko goto fail2;
5315e111ed8SAndrew Rybchenko }
5325e111ed8SAndrew Rybchenko
5335e111ed8SAndrew Rybchenko /*
5345e111ed8SAndrew Rybchenko * Check if the buffer contains an image in signed format, and if so,
5355e111ed8SAndrew Rybchenko * locate the image header.
5365e111ed8SAndrew Rybchenko */
5375e111ed8SAndrew Rybchenko rc = efx_check_signed_image_header(bufferp, buffer_size,
5385e111ed8SAndrew Rybchenko &image_offset, &image_size);
5395e111ed8SAndrew Rybchenko if (rc == 0) {
5405e111ed8SAndrew Rybchenko /*
5415e111ed8SAndrew Rybchenko * Buffer holds signed image format. Check that the encapsulated
5425e111ed8SAndrew Rybchenko * content contains an unsigned image format header.
5435e111ed8SAndrew Rybchenko */
5445e111ed8SAndrew Rybchenko format = EFX_IMAGE_FORMAT_SIGNED;
5455e111ed8SAndrew Rybchenko } else {
5465e111ed8SAndrew Rybchenko /* Check if the buffer holds image in unsigned image format */
5475e111ed8SAndrew Rybchenko format = EFX_IMAGE_FORMAT_UNSIGNED;
5485e111ed8SAndrew Rybchenko image_offset = 0;
5495e111ed8SAndrew Rybchenko image_size = buffer_size;
5505e111ed8SAndrew Rybchenko }
5515e111ed8SAndrew Rybchenko if (image_offset + image_size > buffer_size) {
5525e111ed8SAndrew Rybchenko rc = E2BIG;
5535e111ed8SAndrew Rybchenko goto fail3;
5545e111ed8SAndrew Rybchenko }
5555e111ed8SAndrew Rybchenko imagep = (uint8_t *)bufferp + image_offset;
5565e111ed8SAndrew Rybchenko
5575e111ed8SAndrew Rybchenko /* Check image layout (image header, code, image trailer) */
5585e111ed8SAndrew Rybchenko rc = efx_check_unsigned_image(imagep, image_size, &headerp, &trailerp);
5595e111ed8SAndrew Rybchenko if (rc != 0)
5605e111ed8SAndrew Rybchenko goto fail4;
5615e111ed8SAndrew Rybchenko
5625e111ed8SAndrew Rybchenko /*
5635e111ed8SAndrew Rybchenko * Signed images are packages consumed directly by the firmware,
5645e111ed8SAndrew Rybchenko * with the exception of MC firmware, where the image must be
5655e111ed8SAndrew Rybchenko * rearranged for booting purposes.
5665e111ed8SAndrew Rybchenko */
5675e111ed8SAndrew Rybchenko if (format == EFX_IMAGE_FORMAT_SIGNED) {
5685e111ed8SAndrew Rybchenko if (headerp->eih_type != FIRMWARE_TYPE_MCFW)
5695e111ed8SAndrew Rybchenko format = EFX_IMAGE_FORMAT_SIGNED_PACKAGE;
5705e111ed8SAndrew Rybchenko }
5715e111ed8SAndrew Rybchenko
5725e111ed8SAndrew Rybchenko /* Return image details */
5735e111ed8SAndrew Rybchenko infop->eii_format = format;
5745e111ed8SAndrew Rybchenko infop->eii_imagep = bufferp;
5755e111ed8SAndrew Rybchenko infop->eii_image_size = buffer_size;
5765e111ed8SAndrew Rybchenko infop->eii_headerp = (efx_image_header_t *)imagep;
5775e111ed8SAndrew Rybchenko
5785e111ed8SAndrew Rybchenko return (0);
5795e111ed8SAndrew Rybchenko
5805e111ed8SAndrew Rybchenko fail4:
5815e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
5825e111ed8SAndrew Rybchenko fail3:
5835e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
5845e111ed8SAndrew Rybchenko fail2:
5855e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
5865e111ed8SAndrew Rybchenko infop->eii_format = EFX_IMAGE_FORMAT_INVALID;
5875e111ed8SAndrew Rybchenko infop->eii_imagep = NULL;
5885e111ed8SAndrew Rybchenko infop->eii_image_size = 0;
5895e111ed8SAndrew Rybchenko
5905e111ed8SAndrew Rybchenko fail1:
5915e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
5925e111ed8SAndrew Rybchenko
5935e111ed8SAndrew Rybchenko return (rc);
5945e111ed8SAndrew Rybchenko }
5955e111ed8SAndrew Rybchenko
5965e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
efx_build_signed_image_write_buffer(__out_bcount (buffer_size)uint8_t * bufferp,__in uint32_t buffer_size,__in efx_image_info_t * infop,__out efx_image_header_t ** headerpp)5975e111ed8SAndrew Rybchenko efx_build_signed_image_write_buffer(
5985e111ed8SAndrew Rybchenko __out_bcount(buffer_size)
5995e111ed8SAndrew Rybchenko uint8_t *bufferp,
6005e111ed8SAndrew Rybchenko __in uint32_t buffer_size,
6015e111ed8SAndrew Rybchenko __in efx_image_info_t *infop,
6025e111ed8SAndrew Rybchenko __out efx_image_header_t **headerpp)
6035e111ed8SAndrew Rybchenko {
6045e111ed8SAndrew Rybchenko signed_image_chunk_hdr_t chunk_hdr;
6055e111ed8SAndrew Rybchenko uint32_t hdr_offset;
6065e111ed8SAndrew Rybchenko struct {
6075e111ed8SAndrew Rybchenko uint32_t offset;
6085e111ed8SAndrew Rybchenko uint32_t size;
6095e111ed8SAndrew Rybchenko } cms_header, image_header, code, image_trailer, signature;
6105e111ed8SAndrew Rybchenko efx_rc_t rc;
6115e111ed8SAndrew Rybchenko
6125e111ed8SAndrew Rybchenko EFSYS_ASSERT((infop != NULL) && (headerpp != NULL));
6135e111ed8SAndrew Rybchenko
6145e111ed8SAndrew Rybchenko if ((bufferp == NULL) || (buffer_size == 0) ||
6155e111ed8SAndrew Rybchenko (infop == NULL) || (headerpp == NULL)) {
6165e111ed8SAndrew Rybchenko /* Invalid arguments */
6175e111ed8SAndrew Rybchenko rc = EINVAL;
6185e111ed8SAndrew Rybchenko goto fail1;
6195e111ed8SAndrew Rybchenko }
6205e111ed8SAndrew Rybchenko if ((infop->eii_format != EFX_IMAGE_FORMAT_SIGNED) ||
6215e111ed8SAndrew Rybchenko (infop->eii_imagep == NULL) ||
6225e111ed8SAndrew Rybchenko (infop->eii_headerp == NULL) ||
6235e111ed8SAndrew Rybchenko ((uint8_t *)infop->eii_headerp < (uint8_t *)infop->eii_imagep) ||
6245e111ed8SAndrew Rybchenko (infop->eii_image_size < EFX_IMAGE_HEADER_SIZE) ||
6255e111ed8SAndrew Rybchenko ((size_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep) >
6265e111ed8SAndrew Rybchenko (infop->eii_image_size - EFX_IMAGE_HEADER_SIZE))) {
6275e111ed8SAndrew Rybchenko /* Invalid image info */
6285e111ed8SAndrew Rybchenko rc = EINVAL;
6295e111ed8SAndrew Rybchenko goto fail2;
6305e111ed8SAndrew Rybchenko }
6315e111ed8SAndrew Rybchenko
6325e111ed8SAndrew Rybchenko /* Locate image chunks in original signed image */
6335e111ed8SAndrew Rybchenko cms_header.offset = 0;
6345e111ed8SAndrew Rybchenko cms_header.size =
6355e111ed8SAndrew Rybchenko (uint32_t)((uint8_t *)infop->eii_headerp - infop->eii_imagep);
6365e111ed8SAndrew Rybchenko if ((cms_header.size > buffer_size) ||
6375e111ed8SAndrew Rybchenko (cms_header.offset > (buffer_size - cms_header.size))) {
6385e111ed8SAndrew Rybchenko rc = EINVAL;
6395e111ed8SAndrew Rybchenko goto fail3;
6405e111ed8SAndrew Rybchenko }
6415e111ed8SAndrew Rybchenko
6425e111ed8SAndrew Rybchenko image_header.offset = cms_header.offset + cms_header.size;
6435e111ed8SAndrew Rybchenko image_header.size = infop->eii_headerp->eih_size;
6445e111ed8SAndrew Rybchenko if ((image_header.size > buffer_size) ||
6455e111ed8SAndrew Rybchenko (image_header.offset > (buffer_size - image_header.size))) {
6465e111ed8SAndrew Rybchenko rc = EINVAL;
6475e111ed8SAndrew Rybchenko goto fail4;
6485e111ed8SAndrew Rybchenko }
6495e111ed8SAndrew Rybchenko
6505e111ed8SAndrew Rybchenko code.offset = image_header.offset + image_header.size;
6515e111ed8SAndrew Rybchenko code.size = infop->eii_headerp->eih_code_size;
6525e111ed8SAndrew Rybchenko if ((code.size > buffer_size) ||
6535e111ed8SAndrew Rybchenko (code.offset > (buffer_size - code.size))) {
6545e111ed8SAndrew Rybchenko rc = EINVAL;
6555e111ed8SAndrew Rybchenko goto fail5;
6565e111ed8SAndrew Rybchenko }
6575e111ed8SAndrew Rybchenko
6585e111ed8SAndrew Rybchenko image_trailer.offset = code.offset + code.size;
6595e111ed8SAndrew Rybchenko image_trailer.size = EFX_IMAGE_TRAILER_SIZE;
6605e111ed8SAndrew Rybchenko if ((image_trailer.size > buffer_size) ||
6615e111ed8SAndrew Rybchenko (image_trailer.offset > (buffer_size - image_trailer.size))) {
6625e111ed8SAndrew Rybchenko rc = EINVAL;
6635e111ed8SAndrew Rybchenko goto fail6;
6645e111ed8SAndrew Rybchenko }
6655e111ed8SAndrew Rybchenko
6665e111ed8SAndrew Rybchenko signature.offset = image_trailer.offset + image_trailer.size;
6675e111ed8SAndrew Rybchenko signature.size = (uint32_t)(infop->eii_image_size - signature.offset);
6685e111ed8SAndrew Rybchenko if ((signature.size > buffer_size) ||
6695e111ed8SAndrew Rybchenko (signature.offset > (buffer_size - signature.size))) {
6705e111ed8SAndrew Rybchenko rc = EINVAL;
6715e111ed8SAndrew Rybchenko goto fail7;
6725e111ed8SAndrew Rybchenko }
6735e111ed8SAndrew Rybchenko
6745e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(infop->eii_image_size, ==, cms_header.size +
6755e111ed8SAndrew Rybchenko image_header.size + code.size + image_trailer.size +
6765e111ed8SAndrew Rybchenko signature.size);
6775e111ed8SAndrew Rybchenko
6785e111ed8SAndrew Rybchenko /* BEGIN CSTYLED */
6795e111ed8SAndrew Rybchenko /*
6805e111ed8SAndrew Rybchenko * Build signed image partition, inserting chunk headers.
6815e111ed8SAndrew Rybchenko *
6825e111ed8SAndrew Rybchenko * Signed Image: Image in NVRAM partition:
6835e111ed8SAndrew Rybchenko *
6845e111ed8SAndrew Rybchenko * +-----------------+ +-----------------+
6855e111ed8SAndrew Rybchenko * | CMS header | | mcfw.update |<----+
6865e111ed8SAndrew Rybchenko * +-----------------+ | | |
6875e111ed8SAndrew Rybchenko * | reflash header | +-----------------+ |
6885e111ed8SAndrew Rybchenko * +-----------------+ | chunk header: |-->--|-+
6895e111ed8SAndrew Rybchenko * | mcfw.update | | REFLASH_TRAILER | | |
6905e111ed8SAndrew Rybchenko * | | +-----------------+ | |
6915e111ed8SAndrew Rybchenko * +-----------------+ +-->| CMS header | | |
6925e111ed8SAndrew Rybchenko * | reflash trailer | | +-----------------+ | |
6935e111ed8SAndrew Rybchenko * +-----------------+ | | chunk header: |->-+ | |
6945e111ed8SAndrew Rybchenko * | signature | | | REFLASH_HEADER | | | |
6955e111ed8SAndrew Rybchenko * +-----------------+ | +-----------------+ | | |
6965e111ed8SAndrew Rybchenko * | | reflash header |<--+ | |
6975e111ed8SAndrew Rybchenko * | +-----------------+ | |
6985e111ed8SAndrew Rybchenko * | | chunk header: |-->--+ |
6995e111ed8SAndrew Rybchenko * | | IMAGE | |
7005e111ed8SAndrew Rybchenko * | +-----------------+ |
7015e111ed8SAndrew Rybchenko * | | reflash trailer |<------+
7025e111ed8SAndrew Rybchenko * | +-----------------+
7035e111ed8SAndrew Rybchenko * | | chunk header: |
7045e111ed8SAndrew Rybchenko * | | SIGNATURE |->-+
7055e111ed8SAndrew Rybchenko * | +-----------------+ |
7065e111ed8SAndrew Rybchenko * | | signature |<--+
7075e111ed8SAndrew Rybchenko * | +-----------------+
7085e111ed8SAndrew Rybchenko * | | ...unused... |
7095e111ed8SAndrew Rybchenko * | +-----------------+
7105e111ed8SAndrew Rybchenko * +-<-| chunk header: |
7115e111ed8SAndrew Rybchenko * >-->| CMS_HEADER |
7125e111ed8SAndrew Rybchenko * +-----------------+
7135e111ed8SAndrew Rybchenko *
7145e111ed8SAndrew Rybchenko * Each chunk header gives the partition offset and length of the image
7155e111ed8SAndrew Rybchenko * chunk's data. The image chunk data is immediately followed by the
7165e111ed8SAndrew Rybchenko * chunk header for the next chunk.
7175e111ed8SAndrew Rybchenko *
7185e111ed8SAndrew Rybchenko * The data chunk for the firmware code must be at the start of the
7195e111ed8SAndrew Rybchenko * partition (needed for the bootloader). The first chunk header in the
7205e111ed8SAndrew Rybchenko * chain (for the CMS header) is stored at the end of the partition. The
7215e111ed8SAndrew Rybchenko * chain of chunk headers maintains the same logical order of image
7225e111ed8SAndrew Rybchenko * chunks as the original signed image file. This set of constraints
7235e111ed8SAndrew Rybchenko * results in the layout used for the data chunks and chunk headers.
7245e111ed8SAndrew Rybchenko */
7255e111ed8SAndrew Rybchenko /* END CSTYLED */
7265e111ed8SAndrew Rybchenko memset(bufferp, 0xFF, buffer_size);
7275e111ed8SAndrew Rybchenko
7285e111ed8SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (chunk_hdr) == SIGNED_IMAGE_CHUNK_HDR_LEN);
7295e111ed8SAndrew Rybchenko memset(&chunk_hdr, 0, SIGNED_IMAGE_CHUNK_HDR_LEN);
7305e111ed8SAndrew Rybchenko
7315e111ed8SAndrew Rybchenko /*
7325e111ed8SAndrew Rybchenko * CMS header
7335e111ed8SAndrew Rybchenko */
7345e111ed8SAndrew Rybchenko if (buffer_size < SIGNED_IMAGE_CHUNK_HDR_LEN) {
7355e111ed8SAndrew Rybchenko rc = ENOSPC;
7365e111ed8SAndrew Rybchenko goto fail8;
7375e111ed8SAndrew Rybchenko }
7385e111ed8SAndrew Rybchenko hdr_offset = buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN;
7395e111ed8SAndrew Rybchenko
7405e111ed8SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
7415e111ed8SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
7425e111ed8SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_CMS_HEADER;
7435e111ed8SAndrew Rybchenko chunk_hdr.offset = code.size + SIGNED_IMAGE_CHUNK_HDR_LEN;
7445e111ed8SAndrew Rybchenko chunk_hdr.len = cms_header.size;
7455e111ed8SAndrew Rybchenko
7465e111ed8SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, sizeof (chunk_hdr));
7475e111ed8SAndrew Rybchenko
7485e111ed8SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) ||
7495e111ed8SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
7505e111ed8SAndrew Rybchenko rc = ENOSPC;
7515e111ed8SAndrew Rybchenko goto fail9;
7525e111ed8SAndrew Rybchenko }
7535e111ed8SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset,
7545e111ed8SAndrew Rybchenko infop->eii_imagep + cms_header.offset,
7555e111ed8SAndrew Rybchenko cms_header.size);
7565e111ed8SAndrew Rybchenko
7575e111ed8SAndrew Rybchenko /*
7585e111ed8SAndrew Rybchenko * Image header
7595e111ed8SAndrew Rybchenko */
7605e111ed8SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len;
7615e111ed8SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
7625e111ed8SAndrew Rybchenko rc = ENOSPC;
7635e111ed8SAndrew Rybchenko goto fail10;
7645e111ed8SAndrew Rybchenko }
7655e111ed8SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
7665e111ed8SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
7675e111ed8SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_HEADER;
7685e111ed8SAndrew Rybchenko chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
7695e111ed8SAndrew Rybchenko chunk_hdr.len = image_header.size;
7705e111ed8SAndrew Rybchenko
7715e111ed8SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
7725e111ed8SAndrew Rybchenko
7735e111ed8SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) ||
7745e111ed8SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
7755e111ed8SAndrew Rybchenko rc = ENOSPC;
7765e111ed8SAndrew Rybchenko goto fail11;
7775e111ed8SAndrew Rybchenko }
7785e111ed8SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset,
7795e111ed8SAndrew Rybchenko infop->eii_imagep + image_header.offset,
7805e111ed8SAndrew Rybchenko image_header.size);
7815e111ed8SAndrew Rybchenko
7825e111ed8SAndrew Rybchenko *headerpp = (efx_image_header_t *)(bufferp + chunk_hdr.offset);
7835e111ed8SAndrew Rybchenko
7845e111ed8SAndrew Rybchenko /*
7855e111ed8SAndrew Rybchenko * Firmware code
7865e111ed8SAndrew Rybchenko */
7875e111ed8SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len;
7885e111ed8SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
7895e111ed8SAndrew Rybchenko rc = ENOSPC;
7905e111ed8SAndrew Rybchenko goto fail12;
7915e111ed8SAndrew Rybchenko }
7925e111ed8SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
7935e111ed8SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
7945e111ed8SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_IMAGE;
7955e111ed8SAndrew Rybchenko chunk_hdr.offset = 0;
7965e111ed8SAndrew Rybchenko chunk_hdr.len = code.size;
7975e111ed8SAndrew Rybchenko
7985e111ed8SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
7995e111ed8SAndrew Rybchenko
8005e111ed8SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) ||
8015e111ed8SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
8025e111ed8SAndrew Rybchenko rc = ENOSPC;
8035e111ed8SAndrew Rybchenko goto fail13;
8045e111ed8SAndrew Rybchenko }
8055e111ed8SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset,
8065e111ed8SAndrew Rybchenko infop->eii_imagep + code.offset,
8075e111ed8SAndrew Rybchenko code.size);
8085e111ed8SAndrew Rybchenko
8095e111ed8SAndrew Rybchenko /*
8105e111ed8SAndrew Rybchenko * Image trailer (CRC)
8115e111ed8SAndrew Rybchenko */
8125e111ed8SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
8135e111ed8SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
8145e111ed8SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_REFLASH_TRAILER;
8155e111ed8SAndrew Rybchenko chunk_hdr.offset = hdr_offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
8165e111ed8SAndrew Rybchenko chunk_hdr.len = image_trailer.size;
8175e111ed8SAndrew Rybchenko
8185e111ed8SAndrew Rybchenko hdr_offset = code.size;
8195e111ed8SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
8205e111ed8SAndrew Rybchenko rc = ENOSPC;
8215e111ed8SAndrew Rybchenko goto fail14;
8225e111ed8SAndrew Rybchenko }
8235e111ed8SAndrew Rybchenko
8245e111ed8SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
8255e111ed8SAndrew Rybchenko
8265e111ed8SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) ||
8275e111ed8SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
8285e111ed8SAndrew Rybchenko rc = ENOSPC;
8295e111ed8SAndrew Rybchenko goto fail15;
8305e111ed8SAndrew Rybchenko }
8315e111ed8SAndrew Rybchenko memcpy((uint8_t *)bufferp + chunk_hdr.offset,
8325e111ed8SAndrew Rybchenko infop->eii_imagep + image_trailer.offset,
8335e111ed8SAndrew Rybchenko image_trailer.size);
8345e111ed8SAndrew Rybchenko
8355e111ed8SAndrew Rybchenko /*
8365e111ed8SAndrew Rybchenko * Signature
8375e111ed8SAndrew Rybchenko */
8385e111ed8SAndrew Rybchenko hdr_offset = chunk_hdr.offset + chunk_hdr.len;
8395e111ed8SAndrew Rybchenko if (hdr_offset > (buffer_size - SIGNED_IMAGE_CHUNK_HDR_LEN)) {
8405e111ed8SAndrew Rybchenko rc = ENOSPC;
8415e111ed8SAndrew Rybchenko goto fail16;
8425e111ed8SAndrew Rybchenko }
8435e111ed8SAndrew Rybchenko chunk_hdr.magic = SIGNED_IMAGE_CHUNK_HDR_MAGIC;
8445e111ed8SAndrew Rybchenko chunk_hdr.version = SIGNED_IMAGE_CHUNK_HDR_VERSION;
8455e111ed8SAndrew Rybchenko chunk_hdr.id = SIGNED_IMAGE_CHUNK_SIGNATURE;
8465e111ed8SAndrew Rybchenko chunk_hdr.offset = chunk_hdr.offset + SIGNED_IMAGE_CHUNK_HDR_LEN;
8475e111ed8SAndrew Rybchenko chunk_hdr.len = signature.size;
8485e111ed8SAndrew Rybchenko
8495e111ed8SAndrew Rybchenko memcpy(bufferp + hdr_offset, &chunk_hdr, SIGNED_IMAGE_CHUNK_HDR_LEN);
8505e111ed8SAndrew Rybchenko
8515e111ed8SAndrew Rybchenko if ((chunk_hdr.len > buffer_size) ||
8525e111ed8SAndrew Rybchenko (chunk_hdr.offset > (buffer_size - chunk_hdr.len))) {
8535e111ed8SAndrew Rybchenko rc = ENOSPC;
8545e111ed8SAndrew Rybchenko goto fail17;
8555e111ed8SAndrew Rybchenko }
8565e111ed8SAndrew Rybchenko memcpy(bufferp + chunk_hdr.offset,
8575e111ed8SAndrew Rybchenko infop->eii_imagep + signature.offset,
8585e111ed8SAndrew Rybchenko signature.size);
8595e111ed8SAndrew Rybchenko
8605e111ed8SAndrew Rybchenko return (0);
8615e111ed8SAndrew Rybchenko
8625e111ed8SAndrew Rybchenko fail17:
8635e111ed8SAndrew Rybchenko EFSYS_PROBE(fail17);
8645e111ed8SAndrew Rybchenko fail16:
8655e111ed8SAndrew Rybchenko EFSYS_PROBE(fail16);
8665e111ed8SAndrew Rybchenko fail15:
8675e111ed8SAndrew Rybchenko EFSYS_PROBE(fail15);
8685e111ed8SAndrew Rybchenko fail14:
8695e111ed8SAndrew Rybchenko EFSYS_PROBE(fail14);
8705e111ed8SAndrew Rybchenko fail13:
8715e111ed8SAndrew Rybchenko EFSYS_PROBE(fail13);
8725e111ed8SAndrew Rybchenko fail12:
8735e111ed8SAndrew Rybchenko EFSYS_PROBE(fail12);
8745e111ed8SAndrew Rybchenko fail11:
8755e111ed8SAndrew Rybchenko EFSYS_PROBE(fail11);
8765e111ed8SAndrew Rybchenko fail10:
8775e111ed8SAndrew Rybchenko EFSYS_PROBE(fail10);
8785e111ed8SAndrew Rybchenko fail9:
8795e111ed8SAndrew Rybchenko EFSYS_PROBE(fail9);
8805e111ed8SAndrew Rybchenko fail8:
8815e111ed8SAndrew Rybchenko EFSYS_PROBE(fail8);
8825e111ed8SAndrew Rybchenko fail7:
8835e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
8845e111ed8SAndrew Rybchenko fail6:
8855e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
8865e111ed8SAndrew Rybchenko fail5:
8875e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
8885e111ed8SAndrew Rybchenko fail4:
8895e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
8905e111ed8SAndrew Rybchenko fail3:
8915e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
8925e111ed8SAndrew Rybchenko fail2:
8935e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
8945e111ed8SAndrew Rybchenko fail1:
8955e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
8965e111ed8SAndrew Rybchenko
8975e111ed8SAndrew Rybchenko return (rc);
8985e111ed8SAndrew Rybchenko }
8995e111ed8SAndrew Rybchenko
9005e111ed8SAndrew Rybchenko
9015e111ed8SAndrew Rybchenko
9025e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_IMAGE_LAYOUT */
9035e111ed8SAndrew Rybchenko
9045e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_MEDFORD || EFSYS_OPT_MEDFORD2 */
905