15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko *
3*672386c1SAndrew Rybchenko * Copyright(c) 2019-2021 Xilinx, Inc.
45e111ed8SAndrew Rybchenko * Copyright(c) 2012-2019 Solarflare Communications Inc.
55e111ed8SAndrew Rybchenko */
65e111ed8SAndrew Rybchenko
75e111ed8SAndrew Rybchenko #include "efx.h"
85e111ed8SAndrew Rybchenko #include "efx_impl.h"
95e111ed8SAndrew Rybchenko
105e111ed8SAndrew Rybchenko #if EFX_OPTS_EF10()
115e111ed8SAndrew Rybchenko
125e111ed8SAndrew Rybchenko #if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
135e111ed8SAndrew Rybchenko
145e111ed8SAndrew Rybchenko #include "ef10_tlv_layout.h"
155e111ed8SAndrew Rybchenko
165e111ed8SAndrew Rybchenko /* Cursor for TLV partition format */
175e111ed8SAndrew Rybchenko typedef struct tlv_cursor_s {
185e111ed8SAndrew Rybchenko uint32_t *block; /* Base of data block */
195e111ed8SAndrew Rybchenko uint32_t *current; /* Cursor position */
205e111ed8SAndrew Rybchenko uint32_t *end; /* End tag position */
215e111ed8SAndrew Rybchenko uint32_t *limit; /* Last dword of data block */
225e111ed8SAndrew Rybchenko } tlv_cursor_t;
235e111ed8SAndrew Rybchenko
245e111ed8SAndrew Rybchenko typedef struct nvram_partition_s {
255e111ed8SAndrew Rybchenko uint16_t type;
265e111ed8SAndrew Rybchenko uint8_t chip_select;
275e111ed8SAndrew Rybchenko uint8_t flags;
285e111ed8SAndrew Rybchenko /*
295e111ed8SAndrew Rybchenko * The full length of the NVRAM partition.
305e111ed8SAndrew Rybchenko * This is different from tlv_partition_header.total_length,
315e111ed8SAndrew Rybchenko * which can be smaller.
325e111ed8SAndrew Rybchenko */
335e111ed8SAndrew Rybchenko uint32_t length;
345e111ed8SAndrew Rybchenko uint32_t erase_size;
355e111ed8SAndrew Rybchenko uint32_t *data;
365e111ed8SAndrew Rybchenko tlv_cursor_t tlv_cursor;
375e111ed8SAndrew Rybchenko } nvram_partition_t;
385e111ed8SAndrew Rybchenko
395e111ed8SAndrew Rybchenko
405e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
415e111ed8SAndrew Rybchenko tlv_validate_state(
425e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor);
435e111ed8SAndrew Rybchenko
445e111ed8SAndrew Rybchenko
455e111ed8SAndrew Rybchenko static void
tlv_init_block(__out uint32_t * block)465e111ed8SAndrew Rybchenko tlv_init_block(
475e111ed8SAndrew Rybchenko __out uint32_t *block)
485e111ed8SAndrew Rybchenko {
495e111ed8SAndrew Rybchenko *block = __CPU_TO_LE_32(TLV_TAG_END);
505e111ed8SAndrew Rybchenko }
515e111ed8SAndrew Rybchenko
525e111ed8SAndrew Rybchenko static uint32_t
tlv_tag(__in tlv_cursor_t * cursor)535e111ed8SAndrew Rybchenko tlv_tag(
545e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
555e111ed8SAndrew Rybchenko {
565e111ed8SAndrew Rybchenko uint32_t dword, tag;
575e111ed8SAndrew Rybchenko
585e111ed8SAndrew Rybchenko dword = cursor->current[0];
595e111ed8SAndrew Rybchenko tag = __LE_TO_CPU_32(dword);
605e111ed8SAndrew Rybchenko
615e111ed8SAndrew Rybchenko return (tag);
625e111ed8SAndrew Rybchenko }
635e111ed8SAndrew Rybchenko
645e111ed8SAndrew Rybchenko static size_t
tlv_length(__in tlv_cursor_t * cursor)655e111ed8SAndrew Rybchenko tlv_length(
665e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
675e111ed8SAndrew Rybchenko {
685e111ed8SAndrew Rybchenko uint32_t dword, length;
695e111ed8SAndrew Rybchenko
705e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == TLV_TAG_END)
715e111ed8SAndrew Rybchenko return (0);
725e111ed8SAndrew Rybchenko
735e111ed8SAndrew Rybchenko dword = cursor->current[1];
745e111ed8SAndrew Rybchenko length = __LE_TO_CPU_32(dword);
755e111ed8SAndrew Rybchenko
765e111ed8SAndrew Rybchenko return ((size_t)length);
775e111ed8SAndrew Rybchenko }
785e111ed8SAndrew Rybchenko
795e111ed8SAndrew Rybchenko static uint8_t *
tlv_value(__in tlv_cursor_t * cursor)805e111ed8SAndrew Rybchenko tlv_value(
815e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
825e111ed8SAndrew Rybchenko {
835e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == TLV_TAG_END)
845e111ed8SAndrew Rybchenko return (NULL);
855e111ed8SAndrew Rybchenko
865e111ed8SAndrew Rybchenko return ((uint8_t *)(&cursor->current[2]));
875e111ed8SAndrew Rybchenko }
885e111ed8SAndrew Rybchenko
895e111ed8SAndrew Rybchenko static uint8_t *
tlv_item(__in tlv_cursor_t * cursor)905e111ed8SAndrew Rybchenko tlv_item(
915e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
925e111ed8SAndrew Rybchenko {
935e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == TLV_TAG_END)
945e111ed8SAndrew Rybchenko return (NULL);
955e111ed8SAndrew Rybchenko
965e111ed8SAndrew Rybchenko return ((uint8_t *)cursor->current);
975e111ed8SAndrew Rybchenko }
985e111ed8SAndrew Rybchenko
995e111ed8SAndrew Rybchenko /*
1005e111ed8SAndrew Rybchenko * TLV item DWORD length is tag + length + value (rounded up to DWORD)
1015e111ed8SAndrew Rybchenko * equivalent to tlv_n_words_for_len in mc-comms tlv.c
1025e111ed8SAndrew Rybchenko */
1035e111ed8SAndrew Rybchenko #define TLV_DWORD_COUNT(length) \
1045e111ed8SAndrew Rybchenko (1 + 1 + (((length) + sizeof (uint32_t) - 1) / sizeof (uint32_t)))
1055e111ed8SAndrew Rybchenko
1065e111ed8SAndrew Rybchenko
1075e111ed8SAndrew Rybchenko static uint32_t *
tlv_next_item_ptr(__in tlv_cursor_t * cursor)1085e111ed8SAndrew Rybchenko tlv_next_item_ptr(
1095e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
1105e111ed8SAndrew Rybchenko {
1115e111ed8SAndrew Rybchenko uint32_t length;
1125e111ed8SAndrew Rybchenko
1135e111ed8SAndrew Rybchenko length = tlv_length(cursor);
1145e111ed8SAndrew Rybchenko
1155e111ed8SAndrew Rybchenko return (cursor->current + TLV_DWORD_COUNT(length));
1165e111ed8SAndrew Rybchenko }
1175e111ed8SAndrew Rybchenko
1185e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_advance(__inout tlv_cursor_t * cursor)1195e111ed8SAndrew Rybchenko tlv_advance(
1205e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor)
1215e111ed8SAndrew Rybchenko {
1225e111ed8SAndrew Rybchenko efx_rc_t rc;
1235e111ed8SAndrew Rybchenko
1245e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
1255e111ed8SAndrew Rybchenko goto fail1;
1265e111ed8SAndrew Rybchenko
1275e111ed8SAndrew Rybchenko if (cursor->current == cursor->end) {
1285e111ed8SAndrew Rybchenko /* No more tags after END tag */
1295e111ed8SAndrew Rybchenko cursor->current = NULL;
1305e111ed8SAndrew Rybchenko rc = ENOENT;
1315e111ed8SAndrew Rybchenko goto fail2;
1325e111ed8SAndrew Rybchenko }
1335e111ed8SAndrew Rybchenko
1345e111ed8SAndrew Rybchenko /* Advance to next item and validate */
1355e111ed8SAndrew Rybchenko cursor->current = tlv_next_item_ptr(cursor);
1365e111ed8SAndrew Rybchenko
1375e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
1385e111ed8SAndrew Rybchenko goto fail3;
1395e111ed8SAndrew Rybchenko
1405e111ed8SAndrew Rybchenko return (0);
1415e111ed8SAndrew Rybchenko
1425e111ed8SAndrew Rybchenko fail3:
1435e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
1445e111ed8SAndrew Rybchenko fail2:
1455e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
1465e111ed8SAndrew Rybchenko fail1:
1475e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
1485e111ed8SAndrew Rybchenko
1495e111ed8SAndrew Rybchenko return (rc);
1505e111ed8SAndrew Rybchenko }
1515e111ed8SAndrew Rybchenko
1525e111ed8SAndrew Rybchenko static efx_rc_t
tlv_rewind(__in tlv_cursor_t * cursor)1535e111ed8SAndrew Rybchenko tlv_rewind(
1545e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
1555e111ed8SAndrew Rybchenko {
1565e111ed8SAndrew Rybchenko efx_rc_t rc;
1575e111ed8SAndrew Rybchenko
1585e111ed8SAndrew Rybchenko cursor->current = cursor->block;
1595e111ed8SAndrew Rybchenko
1605e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
1615e111ed8SAndrew Rybchenko goto fail1;
1625e111ed8SAndrew Rybchenko
1635e111ed8SAndrew Rybchenko return (0);
1645e111ed8SAndrew Rybchenko
1655e111ed8SAndrew Rybchenko fail1:
1665e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
1675e111ed8SAndrew Rybchenko
1685e111ed8SAndrew Rybchenko return (rc);
1695e111ed8SAndrew Rybchenko }
1705e111ed8SAndrew Rybchenko
1715e111ed8SAndrew Rybchenko static efx_rc_t
tlv_find(__inout tlv_cursor_t * cursor,__in uint32_t tag)1725e111ed8SAndrew Rybchenko tlv_find(
1735e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor,
1745e111ed8SAndrew Rybchenko __in uint32_t tag)
1755e111ed8SAndrew Rybchenko {
1765e111ed8SAndrew Rybchenko efx_rc_t rc;
1775e111ed8SAndrew Rybchenko
1785e111ed8SAndrew Rybchenko rc = tlv_rewind(cursor);
1795e111ed8SAndrew Rybchenko while (rc == 0) {
1805e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == tag)
1815e111ed8SAndrew Rybchenko break;
1825e111ed8SAndrew Rybchenko
1835e111ed8SAndrew Rybchenko rc = tlv_advance(cursor);
1845e111ed8SAndrew Rybchenko }
1855e111ed8SAndrew Rybchenko return (rc);
1865e111ed8SAndrew Rybchenko }
1875e111ed8SAndrew Rybchenko
1885e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_validate_state(__inout tlv_cursor_t * cursor)1895e111ed8SAndrew Rybchenko tlv_validate_state(
1905e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor)
1915e111ed8SAndrew Rybchenko {
1925e111ed8SAndrew Rybchenko efx_rc_t rc;
1935e111ed8SAndrew Rybchenko
1945e111ed8SAndrew Rybchenko /* Check cursor position */
1955e111ed8SAndrew Rybchenko if (cursor->current < cursor->block) {
1965e111ed8SAndrew Rybchenko rc = EINVAL;
1975e111ed8SAndrew Rybchenko goto fail1;
1985e111ed8SAndrew Rybchenko }
1995e111ed8SAndrew Rybchenko if (cursor->current > cursor->limit) {
2005e111ed8SAndrew Rybchenko rc = EINVAL;
2015e111ed8SAndrew Rybchenko goto fail2;
2025e111ed8SAndrew Rybchenko }
2035e111ed8SAndrew Rybchenko
2045e111ed8SAndrew Rybchenko if (tlv_tag(cursor) != TLV_TAG_END) {
2055e111ed8SAndrew Rybchenko /* Check current item has space for tag and length */
2065e111ed8SAndrew Rybchenko if (cursor->current > (cursor->limit - 1)) {
2075e111ed8SAndrew Rybchenko cursor->current = NULL;
2085e111ed8SAndrew Rybchenko rc = EFAULT;
2095e111ed8SAndrew Rybchenko goto fail3;
2105e111ed8SAndrew Rybchenko }
2115e111ed8SAndrew Rybchenko
2125e111ed8SAndrew Rybchenko /* Check we have value data for current item and an END tag */
2135e111ed8SAndrew Rybchenko if (tlv_next_item_ptr(cursor) > cursor->limit) {
2145e111ed8SAndrew Rybchenko cursor->current = NULL;
2155e111ed8SAndrew Rybchenko rc = EFAULT;
2165e111ed8SAndrew Rybchenko goto fail4;
2175e111ed8SAndrew Rybchenko }
2185e111ed8SAndrew Rybchenko }
2195e111ed8SAndrew Rybchenko
2205e111ed8SAndrew Rybchenko return (0);
2215e111ed8SAndrew Rybchenko
2225e111ed8SAndrew Rybchenko fail4:
2235e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
2245e111ed8SAndrew Rybchenko fail3:
2255e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
2265e111ed8SAndrew Rybchenko fail2:
2275e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
2285e111ed8SAndrew Rybchenko fail1:
2295e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2305e111ed8SAndrew Rybchenko
2315e111ed8SAndrew Rybchenko return (rc);
2325e111ed8SAndrew Rybchenko }
2335e111ed8SAndrew Rybchenko
2345e111ed8SAndrew Rybchenko static efx_rc_t
tlv_init_cursor(__out tlv_cursor_t * cursor,__in uint32_t * block,__in uint32_t * limit,__in uint32_t * current)2355e111ed8SAndrew Rybchenko tlv_init_cursor(
2365e111ed8SAndrew Rybchenko __out tlv_cursor_t *cursor,
2375e111ed8SAndrew Rybchenko __in uint32_t *block,
2385e111ed8SAndrew Rybchenko __in uint32_t *limit,
2395e111ed8SAndrew Rybchenko __in uint32_t *current)
2405e111ed8SAndrew Rybchenko {
2415e111ed8SAndrew Rybchenko cursor->block = block;
2425e111ed8SAndrew Rybchenko cursor->limit = limit;
2435e111ed8SAndrew Rybchenko
2445e111ed8SAndrew Rybchenko cursor->current = current;
2455e111ed8SAndrew Rybchenko cursor->end = NULL;
2465e111ed8SAndrew Rybchenko
2475e111ed8SAndrew Rybchenko return (tlv_validate_state(cursor));
2485e111ed8SAndrew Rybchenko }
2495e111ed8SAndrew Rybchenko
2505e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_init_cursor_from_size(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size)2515e111ed8SAndrew Rybchenko tlv_init_cursor_from_size(
2525e111ed8SAndrew Rybchenko __out tlv_cursor_t *cursor,
2535e111ed8SAndrew Rybchenko __in_bcount(size)
2545e111ed8SAndrew Rybchenko uint8_t *block,
2555e111ed8SAndrew Rybchenko __in size_t size)
2565e111ed8SAndrew Rybchenko {
2575e111ed8SAndrew Rybchenko uint32_t *limit;
2585e111ed8SAndrew Rybchenko limit = (uint32_t *)(block + size - sizeof (uint32_t));
2595e111ed8SAndrew Rybchenko return (tlv_init_cursor(cursor, (uint32_t *)block,
2605e111ed8SAndrew Rybchenko limit, (uint32_t *)block));
2615e111ed8SAndrew Rybchenko }
2625e111ed8SAndrew Rybchenko
2635e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_init_cursor_at_offset(__out tlv_cursor_t * cursor,__in_bcount (size)uint8_t * block,__in size_t size,__in size_t offset)2645e111ed8SAndrew Rybchenko tlv_init_cursor_at_offset(
2655e111ed8SAndrew Rybchenko __out tlv_cursor_t *cursor,
2665e111ed8SAndrew Rybchenko __in_bcount(size)
2675e111ed8SAndrew Rybchenko uint8_t *block,
2685e111ed8SAndrew Rybchenko __in size_t size,
2695e111ed8SAndrew Rybchenko __in size_t offset)
2705e111ed8SAndrew Rybchenko {
2715e111ed8SAndrew Rybchenko uint32_t *limit;
2725e111ed8SAndrew Rybchenko uint32_t *current;
2735e111ed8SAndrew Rybchenko limit = (uint32_t *)(block + size - sizeof (uint32_t));
2745e111ed8SAndrew Rybchenko current = (uint32_t *)(block + offset);
2755e111ed8SAndrew Rybchenko return (tlv_init_cursor(cursor, (uint32_t *)block, limit, current));
2765e111ed8SAndrew Rybchenko }
2775e111ed8SAndrew Rybchenko
2785e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_require_end(__inout tlv_cursor_t * cursor)2795e111ed8SAndrew Rybchenko tlv_require_end(
2805e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor)
2815e111ed8SAndrew Rybchenko {
2825e111ed8SAndrew Rybchenko uint32_t *pos;
2835e111ed8SAndrew Rybchenko efx_rc_t rc;
2845e111ed8SAndrew Rybchenko
2855e111ed8SAndrew Rybchenko if (cursor->end == NULL) {
2865e111ed8SAndrew Rybchenko pos = cursor->current;
2875e111ed8SAndrew Rybchenko if ((rc = tlv_find(cursor, TLV_TAG_END)) != 0)
2885e111ed8SAndrew Rybchenko goto fail1;
2895e111ed8SAndrew Rybchenko
2905e111ed8SAndrew Rybchenko cursor->end = cursor->current;
2915e111ed8SAndrew Rybchenko cursor->current = pos;
2925e111ed8SAndrew Rybchenko }
2935e111ed8SAndrew Rybchenko
2945e111ed8SAndrew Rybchenko return (0);
2955e111ed8SAndrew Rybchenko
2965e111ed8SAndrew Rybchenko fail1:
2975e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2985e111ed8SAndrew Rybchenko
2995e111ed8SAndrew Rybchenko return (rc);
3005e111ed8SAndrew Rybchenko }
3015e111ed8SAndrew Rybchenko
3025e111ed8SAndrew Rybchenko static size_t
tlv_block_length_used(__inout tlv_cursor_t * cursor)3035e111ed8SAndrew Rybchenko tlv_block_length_used(
3045e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor)
3055e111ed8SAndrew Rybchenko {
3065e111ed8SAndrew Rybchenko efx_rc_t rc;
3075e111ed8SAndrew Rybchenko
3085e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
3095e111ed8SAndrew Rybchenko goto fail1;
3105e111ed8SAndrew Rybchenko
3115e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(cursor)) != 0)
3125e111ed8SAndrew Rybchenko goto fail2;
3135e111ed8SAndrew Rybchenko
3145e111ed8SAndrew Rybchenko /* Return space used (including the END tag) */
3155e111ed8SAndrew Rybchenko return (cursor->end + 1 - cursor->block) * sizeof (uint32_t);
3165e111ed8SAndrew Rybchenko
3175e111ed8SAndrew Rybchenko fail2:
3185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
3195e111ed8SAndrew Rybchenko fail1:
3205e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
3215e111ed8SAndrew Rybchenko
3225e111ed8SAndrew Rybchenko return (0);
3235e111ed8SAndrew Rybchenko }
3245e111ed8SAndrew Rybchenko
3255e111ed8SAndrew Rybchenko static uint32_t *
tlv_last_segment_end(__in tlv_cursor_t * cursor)3265e111ed8SAndrew Rybchenko tlv_last_segment_end(
3275e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
3285e111ed8SAndrew Rybchenko {
3295e111ed8SAndrew Rybchenko tlv_cursor_t segment_cursor;
3305e111ed8SAndrew Rybchenko uint32_t *last_segment_end = cursor->block;
3315e111ed8SAndrew Rybchenko uint32_t *segment_start = cursor->block;
3325e111ed8SAndrew Rybchenko
3335e111ed8SAndrew Rybchenko /*
3345e111ed8SAndrew Rybchenko * Go through each segment and check that it has an end tag. If there
3355e111ed8SAndrew Rybchenko * is no end tag then the previous segment was the last valid one,
3365e111ed8SAndrew Rybchenko * so return the pointer to its end tag.
3375e111ed8SAndrew Rybchenko */
3385e111ed8SAndrew Rybchenko for (;;) {
3395e111ed8SAndrew Rybchenko if (tlv_init_cursor(&segment_cursor, segment_start,
3405e111ed8SAndrew Rybchenko cursor->limit, segment_start) != 0)
3415e111ed8SAndrew Rybchenko break;
3425e111ed8SAndrew Rybchenko if (tlv_require_end(&segment_cursor) != 0)
3435e111ed8SAndrew Rybchenko break;
3445e111ed8SAndrew Rybchenko last_segment_end = segment_cursor.end;
3455e111ed8SAndrew Rybchenko segment_start = segment_cursor.end + 1;
3465e111ed8SAndrew Rybchenko }
3475e111ed8SAndrew Rybchenko
3485e111ed8SAndrew Rybchenko return (last_segment_end);
3495e111ed8SAndrew Rybchenko }
3505e111ed8SAndrew Rybchenko
3515e111ed8SAndrew Rybchenko
3525e111ed8SAndrew Rybchenko static uint32_t *
tlv_write(__in tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)3535e111ed8SAndrew Rybchenko tlv_write(
3545e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor,
3555e111ed8SAndrew Rybchenko __in uint32_t tag,
3565e111ed8SAndrew Rybchenko __in_bcount(size) uint8_t *data,
3575e111ed8SAndrew Rybchenko __in size_t size)
3585e111ed8SAndrew Rybchenko {
3595e111ed8SAndrew Rybchenko uint32_t len = size;
3605e111ed8SAndrew Rybchenko uint32_t *ptr;
3615e111ed8SAndrew Rybchenko
3625e111ed8SAndrew Rybchenko ptr = cursor->current;
3635e111ed8SAndrew Rybchenko
3645e111ed8SAndrew Rybchenko *ptr++ = __CPU_TO_LE_32(tag);
3655e111ed8SAndrew Rybchenko *ptr++ = __CPU_TO_LE_32(len);
3665e111ed8SAndrew Rybchenko
3675e111ed8SAndrew Rybchenko if (len > 0) {
3685e111ed8SAndrew Rybchenko ptr[(len - 1) / sizeof (uint32_t)] = 0;
3695e111ed8SAndrew Rybchenko memcpy(ptr, data, len);
3705e111ed8SAndrew Rybchenko ptr += EFX_P2ROUNDUP(uint32_t, len,
3715e111ed8SAndrew Rybchenko sizeof (uint32_t)) / sizeof (*ptr);
3725e111ed8SAndrew Rybchenko }
3735e111ed8SAndrew Rybchenko
3745e111ed8SAndrew Rybchenko return (ptr);
3755e111ed8SAndrew Rybchenko }
3765e111ed8SAndrew Rybchenko
3775e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_insert(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)3785e111ed8SAndrew Rybchenko tlv_insert(
3795e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor,
3805e111ed8SAndrew Rybchenko __in uint32_t tag,
3815e111ed8SAndrew Rybchenko __in_bcount(size)
3825e111ed8SAndrew Rybchenko uint8_t *data,
3835e111ed8SAndrew Rybchenko __in size_t size)
3845e111ed8SAndrew Rybchenko {
3855e111ed8SAndrew Rybchenko unsigned int delta;
3865e111ed8SAndrew Rybchenko uint32_t *last_segment_end;
3875e111ed8SAndrew Rybchenko efx_rc_t rc;
3885e111ed8SAndrew Rybchenko
3895e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
3905e111ed8SAndrew Rybchenko goto fail1;
3915e111ed8SAndrew Rybchenko
3925e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(cursor)) != 0)
3935e111ed8SAndrew Rybchenko goto fail2;
3945e111ed8SAndrew Rybchenko
3955e111ed8SAndrew Rybchenko if (tag == TLV_TAG_END) {
3965e111ed8SAndrew Rybchenko rc = EINVAL;
3975e111ed8SAndrew Rybchenko goto fail3;
3985e111ed8SAndrew Rybchenko }
3995e111ed8SAndrew Rybchenko
4005e111ed8SAndrew Rybchenko last_segment_end = tlv_last_segment_end(cursor);
4015e111ed8SAndrew Rybchenko
4025e111ed8SAndrew Rybchenko delta = TLV_DWORD_COUNT(size);
4035e111ed8SAndrew Rybchenko if (last_segment_end + 1 + delta > cursor->limit) {
4045e111ed8SAndrew Rybchenko rc = ENOSPC;
4055e111ed8SAndrew Rybchenko goto fail4;
4065e111ed8SAndrew Rybchenko }
4075e111ed8SAndrew Rybchenko
4085e111ed8SAndrew Rybchenko /* Move data up: new space at cursor->current */
4095e111ed8SAndrew Rybchenko memmove(cursor->current + delta, cursor->current,
4105e111ed8SAndrew Rybchenko (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
4115e111ed8SAndrew Rybchenko
4125e111ed8SAndrew Rybchenko /* Adjust the end pointer */
4135e111ed8SAndrew Rybchenko cursor->end += delta;
4145e111ed8SAndrew Rybchenko
4155e111ed8SAndrew Rybchenko /* Write new TLV item */
4165e111ed8SAndrew Rybchenko tlv_write(cursor, tag, data, size);
4175e111ed8SAndrew Rybchenko
4185e111ed8SAndrew Rybchenko return (0);
4195e111ed8SAndrew Rybchenko
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
tlv_delete(__inout tlv_cursor_t * cursor)4335e111ed8SAndrew Rybchenko tlv_delete(
4345e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor)
4355e111ed8SAndrew Rybchenko {
4365e111ed8SAndrew Rybchenko unsigned int delta;
4375e111ed8SAndrew Rybchenko uint32_t *last_segment_end;
4385e111ed8SAndrew Rybchenko efx_rc_t rc;
4395e111ed8SAndrew Rybchenko
4405e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
4415e111ed8SAndrew Rybchenko goto fail1;
4425e111ed8SAndrew Rybchenko
4435e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == TLV_TAG_END) {
4445e111ed8SAndrew Rybchenko rc = EINVAL;
4455e111ed8SAndrew Rybchenko goto fail2;
4465e111ed8SAndrew Rybchenko }
4475e111ed8SAndrew Rybchenko
4485e111ed8SAndrew Rybchenko delta = TLV_DWORD_COUNT(tlv_length(cursor));
4495e111ed8SAndrew Rybchenko
4505e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(cursor)) != 0)
4515e111ed8SAndrew Rybchenko goto fail3;
4525e111ed8SAndrew Rybchenko
4535e111ed8SAndrew Rybchenko last_segment_end = tlv_last_segment_end(cursor);
4545e111ed8SAndrew Rybchenko
4555e111ed8SAndrew Rybchenko /* Shuffle things down, destroying the item at cursor->current */
4565e111ed8SAndrew Rybchenko memmove(cursor->current, cursor->current + delta,
4575e111ed8SAndrew Rybchenko (last_segment_end + 1 - cursor->current) * sizeof (uint32_t));
4585e111ed8SAndrew Rybchenko /* Zero the new space at the end of the TLV chain */
4595e111ed8SAndrew Rybchenko memset(last_segment_end + 1 - delta, 0, delta * sizeof (uint32_t));
4605e111ed8SAndrew Rybchenko /* Adjust the end pointer */
4615e111ed8SAndrew Rybchenko cursor->end -= delta;
4625e111ed8SAndrew Rybchenko
4635e111ed8SAndrew Rybchenko return (0);
4645e111ed8SAndrew Rybchenko
4655e111ed8SAndrew Rybchenko fail3:
4665e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
4675e111ed8SAndrew Rybchenko fail2:
4685e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
4695e111ed8SAndrew Rybchenko fail1:
4705e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
4715e111ed8SAndrew Rybchenko
4725e111ed8SAndrew Rybchenko return (rc);
4735e111ed8SAndrew Rybchenko }
4745e111ed8SAndrew Rybchenko
4755e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_modify(__inout tlv_cursor_t * cursor,__in uint32_t tag,__in_bcount (size)uint8_t * data,__in size_t size)4765e111ed8SAndrew Rybchenko tlv_modify(
4775e111ed8SAndrew Rybchenko __inout tlv_cursor_t *cursor,
4785e111ed8SAndrew Rybchenko __in uint32_t tag,
4795e111ed8SAndrew Rybchenko __in_bcount(size)
4805e111ed8SAndrew Rybchenko uint8_t *data,
4815e111ed8SAndrew Rybchenko __in size_t size)
4825e111ed8SAndrew Rybchenko {
4835e111ed8SAndrew Rybchenko uint32_t *pos;
4845e111ed8SAndrew Rybchenko unsigned int old_ndwords;
4855e111ed8SAndrew Rybchenko unsigned int new_ndwords;
4865e111ed8SAndrew Rybchenko unsigned int delta;
4875e111ed8SAndrew Rybchenko uint32_t *last_segment_end;
4885e111ed8SAndrew Rybchenko efx_rc_t rc;
4895e111ed8SAndrew Rybchenko
4905e111ed8SAndrew Rybchenko if ((rc = tlv_validate_state(cursor)) != 0)
4915e111ed8SAndrew Rybchenko goto fail1;
4925e111ed8SAndrew Rybchenko
4935e111ed8SAndrew Rybchenko if (tlv_tag(cursor) == TLV_TAG_END) {
4945e111ed8SAndrew Rybchenko rc = EINVAL;
4955e111ed8SAndrew Rybchenko goto fail2;
4965e111ed8SAndrew Rybchenko }
4975e111ed8SAndrew Rybchenko if (tlv_tag(cursor) != tag) {
4985e111ed8SAndrew Rybchenko rc = EINVAL;
4995e111ed8SAndrew Rybchenko goto fail3;
5005e111ed8SAndrew Rybchenko }
5015e111ed8SAndrew Rybchenko
5025e111ed8SAndrew Rybchenko old_ndwords = TLV_DWORD_COUNT(tlv_length(cursor));
5035e111ed8SAndrew Rybchenko new_ndwords = TLV_DWORD_COUNT(size);
5045e111ed8SAndrew Rybchenko
5055e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(cursor)) != 0)
5065e111ed8SAndrew Rybchenko goto fail4;
5075e111ed8SAndrew Rybchenko
5085e111ed8SAndrew Rybchenko last_segment_end = tlv_last_segment_end(cursor);
5095e111ed8SAndrew Rybchenko
5105e111ed8SAndrew Rybchenko if (new_ndwords > old_ndwords) {
5115e111ed8SAndrew Rybchenko /* Expand space used for TLV item */
5125e111ed8SAndrew Rybchenko delta = new_ndwords - old_ndwords;
5135e111ed8SAndrew Rybchenko pos = cursor->current + old_ndwords;
5145e111ed8SAndrew Rybchenko
5155e111ed8SAndrew Rybchenko if (last_segment_end + 1 + delta > cursor->limit) {
5165e111ed8SAndrew Rybchenko rc = ENOSPC;
5175e111ed8SAndrew Rybchenko goto fail5;
5185e111ed8SAndrew Rybchenko }
5195e111ed8SAndrew Rybchenko
5205e111ed8SAndrew Rybchenko /* Move up: new space at (cursor->current + old_ndwords) */
5215e111ed8SAndrew Rybchenko memmove(pos + delta, pos,
5225e111ed8SAndrew Rybchenko (last_segment_end + 1 - pos) * sizeof (uint32_t));
5235e111ed8SAndrew Rybchenko
5245e111ed8SAndrew Rybchenko /* Adjust the end pointer */
5255e111ed8SAndrew Rybchenko cursor->end += delta;
5265e111ed8SAndrew Rybchenko
5275e111ed8SAndrew Rybchenko } else if (new_ndwords < old_ndwords) {
5285e111ed8SAndrew Rybchenko /* Shrink space used for TLV item */
5295e111ed8SAndrew Rybchenko delta = old_ndwords - new_ndwords;
5305e111ed8SAndrew Rybchenko pos = cursor->current + new_ndwords;
5315e111ed8SAndrew Rybchenko
5325e111ed8SAndrew Rybchenko /* Move down: remove words at (cursor->current + new_ndwords) */
5335e111ed8SAndrew Rybchenko memmove(pos, pos + delta,
5345e111ed8SAndrew Rybchenko (last_segment_end + 1 - pos) * sizeof (uint32_t));
5355e111ed8SAndrew Rybchenko
5365e111ed8SAndrew Rybchenko /* Zero the new space at the end of the TLV chain */
5375e111ed8SAndrew Rybchenko memset(last_segment_end + 1 - delta, 0,
5385e111ed8SAndrew Rybchenko delta * sizeof (uint32_t));
5395e111ed8SAndrew Rybchenko
5405e111ed8SAndrew Rybchenko /* Adjust the end pointer */
5415e111ed8SAndrew Rybchenko cursor->end -= delta;
5425e111ed8SAndrew Rybchenko }
5435e111ed8SAndrew Rybchenko
5445e111ed8SAndrew Rybchenko /* Write new data */
5455e111ed8SAndrew Rybchenko tlv_write(cursor, tag, data, size);
5465e111ed8SAndrew Rybchenko
5475e111ed8SAndrew Rybchenko return (0);
5485e111ed8SAndrew Rybchenko
5495e111ed8SAndrew Rybchenko fail5:
5505e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
5515e111ed8SAndrew Rybchenko fail4:
5525e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
5535e111ed8SAndrew Rybchenko fail3:
5545e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
5555e111ed8SAndrew Rybchenko fail2:
5565e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
5575e111ed8SAndrew Rybchenko fail1:
5585e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
5595e111ed8SAndrew Rybchenko
5605e111ed8SAndrew Rybchenko return (rc);
5615e111ed8SAndrew Rybchenko }
5625e111ed8SAndrew Rybchenko
checksum_tlv_partition(__in nvram_partition_t * partition)5635e111ed8SAndrew Rybchenko static uint32_t checksum_tlv_partition(
5645e111ed8SAndrew Rybchenko __in nvram_partition_t *partition)
5655e111ed8SAndrew Rybchenko {
5665e111ed8SAndrew Rybchenko tlv_cursor_t *cursor;
5675e111ed8SAndrew Rybchenko uint32_t *ptr;
5685e111ed8SAndrew Rybchenko uint32_t *end;
5695e111ed8SAndrew Rybchenko uint32_t csum;
5705e111ed8SAndrew Rybchenko size_t len;
5715e111ed8SAndrew Rybchenko
5725e111ed8SAndrew Rybchenko cursor = &partition->tlv_cursor;
5735e111ed8SAndrew Rybchenko len = tlv_block_length_used(cursor);
5745e111ed8SAndrew Rybchenko EFSYS_ASSERT3U((len & 3), ==, 0);
5755e111ed8SAndrew Rybchenko
5765e111ed8SAndrew Rybchenko csum = 0;
5775e111ed8SAndrew Rybchenko ptr = partition->data;
5785e111ed8SAndrew Rybchenko end = &ptr[len >> 2];
5795e111ed8SAndrew Rybchenko
5805e111ed8SAndrew Rybchenko while (ptr < end)
5815e111ed8SAndrew Rybchenko csum += __LE_TO_CPU_32(*ptr++);
5825e111ed8SAndrew Rybchenko
5835e111ed8SAndrew Rybchenko return (csum);
5845e111ed8SAndrew Rybchenko }
5855e111ed8SAndrew Rybchenko
5865e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
tlv_update_partition_len_and_cks(__in tlv_cursor_t * cursor)5875e111ed8SAndrew Rybchenko tlv_update_partition_len_and_cks(
5885e111ed8SAndrew Rybchenko __in tlv_cursor_t *cursor)
5895e111ed8SAndrew Rybchenko {
5905e111ed8SAndrew Rybchenko efx_rc_t rc;
5915e111ed8SAndrew Rybchenko nvram_partition_t partition;
5925e111ed8SAndrew Rybchenko struct tlv_partition_header *header;
5935e111ed8SAndrew Rybchenko struct tlv_partition_trailer *trailer;
5945e111ed8SAndrew Rybchenko size_t new_len;
5955e111ed8SAndrew Rybchenko
5965e111ed8SAndrew Rybchenko /*
5975e111ed8SAndrew Rybchenko * We just modified the partition, so the total length may not be
5985e111ed8SAndrew Rybchenko * valid. Don't use tlv_find(), which performs some sanity checks
5995e111ed8SAndrew Rybchenko * that may fail here.
6005e111ed8SAndrew Rybchenko */
6015e111ed8SAndrew Rybchenko partition.data = cursor->block;
6025e111ed8SAndrew Rybchenko memcpy(&partition.tlv_cursor, cursor, sizeof (*cursor));
6035e111ed8SAndrew Rybchenko header = (struct tlv_partition_header *)partition.data;
6045e111ed8SAndrew Rybchenko /* Sanity check. */
6055e111ed8SAndrew Rybchenko if (__LE_TO_CPU_32(header->tag) != TLV_TAG_PARTITION_HEADER) {
6065e111ed8SAndrew Rybchenko rc = EFAULT;
6075e111ed8SAndrew Rybchenko goto fail1;
6085e111ed8SAndrew Rybchenko }
6095e111ed8SAndrew Rybchenko new_len = tlv_block_length_used(&partition.tlv_cursor);
6105e111ed8SAndrew Rybchenko if (new_len == 0) {
6115e111ed8SAndrew Rybchenko rc = EFAULT;
6125e111ed8SAndrew Rybchenko goto fail2;
6135e111ed8SAndrew Rybchenko }
6145e111ed8SAndrew Rybchenko header->total_length = __CPU_TO_LE_32(new_len);
6155e111ed8SAndrew Rybchenko /* Ensure the modified partition always has a new generation count. */
6165e111ed8SAndrew Rybchenko header->generation = __CPU_TO_LE_32(
6175e111ed8SAndrew Rybchenko __LE_TO_CPU_32(header->generation) + 1);
6185e111ed8SAndrew Rybchenko
6195e111ed8SAndrew Rybchenko trailer = (struct tlv_partition_trailer *)((uint8_t *)header +
6205e111ed8SAndrew Rybchenko new_len - sizeof (*trailer) - sizeof (uint32_t));
6215e111ed8SAndrew Rybchenko trailer->generation = header->generation;
6225e111ed8SAndrew Rybchenko trailer->checksum = __CPU_TO_LE_32(
6235e111ed8SAndrew Rybchenko __LE_TO_CPU_32(trailer->checksum) -
6245e111ed8SAndrew Rybchenko checksum_tlv_partition(&partition));
6255e111ed8SAndrew Rybchenko
6265e111ed8SAndrew Rybchenko return (0);
6275e111ed8SAndrew Rybchenko
6285e111ed8SAndrew Rybchenko fail2:
6295e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
6305e111ed8SAndrew Rybchenko fail1:
6315e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
6325e111ed8SAndrew Rybchenko
6335e111ed8SAndrew Rybchenko return (rc);
6345e111ed8SAndrew Rybchenko }
6355e111ed8SAndrew Rybchenko
6365e111ed8SAndrew Rybchenko /* Validate buffer contents (before writing to flash) */
6375e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_validate(__in uint32_t partn,__in_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)6385e111ed8SAndrew Rybchenko ef10_nvram_buffer_validate(
6395e111ed8SAndrew Rybchenko __in uint32_t partn,
6405e111ed8SAndrew Rybchenko __in_bcount(partn_size) caddr_t partn_data,
6415e111ed8SAndrew Rybchenko __in size_t partn_size)
6425e111ed8SAndrew Rybchenko {
6435e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
6445e111ed8SAndrew Rybchenko struct tlv_partition_header *header;
6455e111ed8SAndrew Rybchenko struct tlv_partition_trailer *trailer;
6465e111ed8SAndrew Rybchenko size_t total_length;
6475e111ed8SAndrew Rybchenko uint32_t cksum;
6485e111ed8SAndrew Rybchenko int pos;
6495e111ed8SAndrew Rybchenko efx_rc_t rc;
6505e111ed8SAndrew Rybchenko
6515e111ed8SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
6525e111ed8SAndrew Rybchenko
6535e111ed8SAndrew Rybchenko if ((partn_data == NULL) || (partn_size == 0)) {
6545e111ed8SAndrew Rybchenko rc = EINVAL;
6555e111ed8SAndrew Rybchenko goto fail1;
6565e111ed8SAndrew Rybchenko }
6575e111ed8SAndrew Rybchenko
6585e111ed8SAndrew Rybchenko /* The partition header must be the first item (at offset zero) */
6595e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)partn_data,
6605e111ed8SAndrew Rybchenko partn_size)) != 0) {
6615e111ed8SAndrew Rybchenko rc = EFAULT;
6625e111ed8SAndrew Rybchenko goto fail2;
6635e111ed8SAndrew Rybchenko }
6645e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
6655e111ed8SAndrew Rybchenko rc = EINVAL;
6665e111ed8SAndrew Rybchenko goto fail3;
6675e111ed8SAndrew Rybchenko }
6685e111ed8SAndrew Rybchenko header = (struct tlv_partition_header *)tlv_item(&cursor);
6695e111ed8SAndrew Rybchenko
6705e111ed8SAndrew Rybchenko /* Check TLV partition length (includes the END tag) */
6715e111ed8SAndrew Rybchenko total_length = __LE_TO_CPU_32(header->total_length);
6725e111ed8SAndrew Rybchenko if (total_length > partn_size) {
6735e111ed8SAndrew Rybchenko rc = EFBIG;
6745e111ed8SAndrew Rybchenko goto fail4;
6755e111ed8SAndrew Rybchenko }
6765e111ed8SAndrew Rybchenko
6775e111ed8SAndrew Rybchenko /* Check partition header matches partn */
6785e111ed8SAndrew Rybchenko if (__LE_TO_CPU_16(header->type_id) != partn) {
6795e111ed8SAndrew Rybchenko rc = EINVAL;
6805e111ed8SAndrew Rybchenko goto fail5;
6815e111ed8SAndrew Rybchenko }
6825e111ed8SAndrew Rybchenko
6835e111ed8SAndrew Rybchenko /* Check partition ends with PARTITION_TRAILER and END tags */
6845e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
6855e111ed8SAndrew Rybchenko rc = EINVAL;
6865e111ed8SAndrew Rybchenko goto fail6;
6875e111ed8SAndrew Rybchenko }
6885e111ed8SAndrew Rybchenko trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
6895e111ed8SAndrew Rybchenko
6905e111ed8SAndrew Rybchenko if ((rc = tlv_advance(&cursor)) != 0) {
6915e111ed8SAndrew Rybchenko rc = EINVAL;
6925e111ed8SAndrew Rybchenko goto fail7;
6935e111ed8SAndrew Rybchenko }
6945e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_END) {
6955e111ed8SAndrew Rybchenko rc = EINVAL;
6965e111ed8SAndrew Rybchenko goto fail8;
6975e111ed8SAndrew Rybchenko }
6985e111ed8SAndrew Rybchenko
6995e111ed8SAndrew Rybchenko /* Check generation counts are consistent */
7005e111ed8SAndrew Rybchenko if (trailer->generation != header->generation) {
7015e111ed8SAndrew Rybchenko rc = EINVAL;
7025e111ed8SAndrew Rybchenko goto fail9;
7035e111ed8SAndrew Rybchenko }
7045e111ed8SAndrew Rybchenko
7055e111ed8SAndrew Rybchenko /* Verify partition checksum */
7065e111ed8SAndrew Rybchenko cksum = 0;
7075e111ed8SAndrew Rybchenko for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
7085e111ed8SAndrew Rybchenko cksum += *((uint32_t *)(partn_data + pos));
7095e111ed8SAndrew Rybchenko }
7105e111ed8SAndrew Rybchenko if (cksum != 0) {
7115e111ed8SAndrew Rybchenko rc = EINVAL;
7125e111ed8SAndrew Rybchenko goto fail10;
7135e111ed8SAndrew Rybchenko }
7145e111ed8SAndrew Rybchenko
7155e111ed8SAndrew Rybchenko return (0);
7165e111ed8SAndrew Rybchenko
7175e111ed8SAndrew Rybchenko fail10:
7185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail10);
7195e111ed8SAndrew Rybchenko fail9:
7205e111ed8SAndrew Rybchenko EFSYS_PROBE(fail9);
7215e111ed8SAndrew Rybchenko fail8:
7225e111ed8SAndrew Rybchenko EFSYS_PROBE(fail8);
7235e111ed8SAndrew Rybchenko fail7:
7245e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
7255e111ed8SAndrew Rybchenko fail6:
7265e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
7275e111ed8SAndrew Rybchenko fail5:
7285e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
7295e111ed8SAndrew Rybchenko fail4:
7305e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
7315e111ed8SAndrew Rybchenko fail3:
7325e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
7335e111ed8SAndrew Rybchenko fail2:
7345e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
7355e111ed8SAndrew Rybchenko fail1:
7365e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
7375e111ed8SAndrew Rybchenko
7385e111ed8SAndrew Rybchenko return (rc);
7395e111ed8SAndrew Rybchenko }
7405e111ed8SAndrew Rybchenko
7415e111ed8SAndrew Rybchenko void
ef10_nvram_buffer_init(__out_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size)7425e111ed8SAndrew Rybchenko ef10_nvram_buffer_init(
7435e111ed8SAndrew Rybchenko __out_bcount(buffer_size)
7445e111ed8SAndrew Rybchenko caddr_t bufferp,
7455e111ed8SAndrew Rybchenko __in size_t buffer_size)
7465e111ed8SAndrew Rybchenko {
7475e111ed8SAndrew Rybchenko uint32_t *buf = (uint32_t *)bufferp;
7485e111ed8SAndrew Rybchenko
7495e111ed8SAndrew Rybchenko memset(buf, 0xff, buffer_size);
7505e111ed8SAndrew Rybchenko
7515e111ed8SAndrew Rybchenko tlv_init_block(buf);
7525e111ed8SAndrew Rybchenko }
7535e111ed8SAndrew Rybchenko
7545e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_create(__in uint32_t partn_type,__out_bcount (partn_size)caddr_t partn_data,__in size_t partn_size)7555e111ed8SAndrew Rybchenko ef10_nvram_buffer_create(
7565e111ed8SAndrew Rybchenko __in uint32_t partn_type,
7575e111ed8SAndrew Rybchenko __out_bcount(partn_size)
7585e111ed8SAndrew Rybchenko caddr_t partn_data,
7595e111ed8SAndrew Rybchenko __in size_t partn_size)
7605e111ed8SAndrew Rybchenko {
7615e111ed8SAndrew Rybchenko uint32_t *buf = (uint32_t *)partn_data;
7625e111ed8SAndrew Rybchenko efx_rc_t rc;
7635e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
7645e111ed8SAndrew Rybchenko struct tlv_partition_header header;
7655e111ed8SAndrew Rybchenko struct tlv_partition_trailer trailer;
7665e111ed8SAndrew Rybchenko
7675e111ed8SAndrew Rybchenko unsigned int min_buf_size = sizeof (struct tlv_partition_header) +
7685e111ed8SAndrew Rybchenko sizeof (struct tlv_partition_trailer);
7695e111ed8SAndrew Rybchenko if (partn_size < min_buf_size) {
7705e111ed8SAndrew Rybchenko rc = EINVAL;
7715e111ed8SAndrew Rybchenko goto fail1;
7725e111ed8SAndrew Rybchenko }
7735e111ed8SAndrew Rybchenko
7745e111ed8SAndrew Rybchenko ef10_nvram_buffer_init(partn_data, partn_size);
7755e111ed8SAndrew Rybchenko
7765e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor(&cursor, buf,
7775e111ed8SAndrew Rybchenko (uint32_t *)((uint8_t *)buf + partn_size),
7785e111ed8SAndrew Rybchenko buf)) != 0) {
7795e111ed8SAndrew Rybchenko goto fail2;
7805e111ed8SAndrew Rybchenko }
7815e111ed8SAndrew Rybchenko
7825e111ed8SAndrew Rybchenko header.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_HEADER);
7835e111ed8SAndrew Rybchenko header.length = __CPU_TO_LE_32(sizeof (header) - 8);
7845e111ed8SAndrew Rybchenko header.type_id = __CPU_TO_LE_16(partn_type);
7855e111ed8SAndrew Rybchenko header.preset = 0;
7865e111ed8SAndrew Rybchenko header.generation = __CPU_TO_LE_32(1);
7875e111ed8SAndrew Rybchenko header.total_length = 0; /* This will be fixed below. */
7885e111ed8SAndrew Rybchenko if ((rc = tlv_insert(
7895e111ed8SAndrew Rybchenko &cursor, TLV_TAG_PARTITION_HEADER,
7905e111ed8SAndrew Rybchenko (uint8_t *)&header.type_id, sizeof (header) - 8)) != 0)
7915e111ed8SAndrew Rybchenko goto fail3;
7925e111ed8SAndrew Rybchenko if ((rc = tlv_advance(&cursor)) != 0)
7935e111ed8SAndrew Rybchenko goto fail4;
7945e111ed8SAndrew Rybchenko
7955e111ed8SAndrew Rybchenko trailer.tag = __CPU_TO_LE_32(TLV_TAG_PARTITION_TRAILER);
7965e111ed8SAndrew Rybchenko trailer.length = __CPU_TO_LE_32(sizeof (trailer) - 8);
7975e111ed8SAndrew Rybchenko trailer.generation = header.generation;
7985e111ed8SAndrew Rybchenko trailer.checksum = 0; /* This will be fixed below. */
7995e111ed8SAndrew Rybchenko if ((rc = tlv_insert(&cursor, TLV_TAG_PARTITION_TRAILER,
8005e111ed8SAndrew Rybchenko (uint8_t *)&trailer.generation, sizeof (trailer) - 8)) != 0)
8015e111ed8SAndrew Rybchenko goto fail5;
8025e111ed8SAndrew Rybchenko
8035e111ed8SAndrew Rybchenko if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
8045e111ed8SAndrew Rybchenko goto fail6;
8055e111ed8SAndrew Rybchenko
8065e111ed8SAndrew Rybchenko /* Check that the partition is valid. */
8075e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_buffer_validate(partn_type,
8085e111ed8SAndrew Rybchenko partn_data, partn_size)) != 0)
8095e111ed8SAndrew Rybchenko goto fail7;
8105e111ed8SAndrew Rybchenko
8115e111ed8SAndrew Rybchenko return (0);
8125e111ed8SAndrew Rybchenko
8135e111ed8SAndrew Rybchenko fail7:
8145e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
8155e111ed8SAndrew Rybchenko fail6:
8165e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
8175e111ed8SAndrew Rybchenko fail5:
8185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
8195e111ed8SAndrew Rybchenko fail4:
8205e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
8215e111ed8SAndrew Rybchenko fail3:
8225e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
8235e111ed8SAndrew Rybchenko fail2:
8245e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
8255e111ed8SAndrew Rybchenko fail1:
8265e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
8275e111ed8SAndrew Rybchenko
8285e111ed8SAndrew Rybchenko return (rc);
8295e111ed8SAndrew Rybchenko }
8305e111ed8SAndrew Rybchenko
8315e111ed8SAndrew Rybchenko static uint32_t
byte_offset(__in uint32_t * position,__in uint32_t * base)8325e111ed8SAndrew Rybchenko byte_offset(
8335e111ed8SAndrew Rybchenko __in uint32_t *position,
8345e111ed8SAndrew Rybchenko __in uint32_t *base)
8355e111ed8SAndrew Rybchenko {
8365e111ed8SAndrew Rybchenko return (uint32_t)((uint8_t *)position - (uint8_t *)base);
8375e111ed8SAndrew Rybchenko }
8385e111ed8SAndrew Rybchenko
8395e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_find_item_start(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__out uint32_t * startp)8405e111ed8SAndrew Rybchenko ef10_nvram_buffer_find_item_start(
8415e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
8425e111ed8SAndrew Rybchenko caddr_t bufferp,
8435e111ed8SAndrew Rybchenko __in size_t buffer_size,
8445e111ed8SAndrew Rybchenko __out uint32_t *startp)
8455e111ed8SAndrew Rybchenko {
8465e111ed8SAndrew Rybchenko /* Read past partition header to find start address of the first key */
8475e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
8485e111ed8SAndrew Rybchenko efx_rc_t rc;
8495e111ed8SAndrew Rybchenko
8505e111ed8SAndrew Rybchenko /* A PARTITION_HEADER tag must be the first item (at offset zero) */
8515e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
8525e111ed8SAndrew Rybchenko buffer_size)) != 0) {
8535e111ed8SAndrew Rybchenko rc = EFAULT;
8545e111ed8SAndrew Rybchenko goto fail1;
8555e111ed8SAndrew Rybchenko }
8565e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
8575e111ed8SAndrew Rybchenko rc = EINVAL;
8585e111ed8SAndrew Rybchenko goto fail2;
8595e111ed8SAndrew Rybchenko }
8605e111ed8SAndrew Rybchenko
8615e111ed8SAndrew Rybchenko if ((rc = tlv_advance(&cursor)) != 0) {
8625e111ed8SAndrew Rybchenko rc = EINVAL;
8635e111ed8SAndrew Rybchenko goto fail3;
8645e111ed8SAndrew Rybchenko }
8655e111ed8SAndrew Rybchenko *startp = byte_offset(cursor.current, cursor.block);
8665e111ed8SAndrew Rybchenko
8675e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(&cursor)) != 0)
8685e111ed8SAndrew Rybchenko goto fail4;
8695e111ed8SAndrew Rybchenko
8705e111ed8SAndrew Rybchenko return (0);
8715e111ed8SAndrew Rybchenko
8725e111ed8SAndrew Rybchenko fail4:
8735e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
8745e111ed8SAndrew Rybchenko fail3:
8755e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
8765e111ed8SAndrew Rybchenko fail2:
8775e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
8785e111ed8SAndrew Rybchenko fail1:
8795e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
8805e111ed8SAndrew Rybchenko
8815e111ed8SAndrew Rybchenko return (rc);
8825e111ed8SAndrew Rybchenko }
8835e111ed8SAndrew Rybchenko
8845e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_find_end(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * endp)8855e111ed8SAndrew Rybchenko ef10_nvram_buffer_find_end(
8865e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
8875e111ed8SAndrew Rybchenko caddr_t bufferp,
8885e111ed8SAndrew Rybchenko __in size_t buffer_size,
8895e111ed8SAndrew Rybchenko __in uint32_t offset,
8905e111ed8SAndrew Rybchenko __out uint32_t *endp)
8915e111ed8SAndrew Rybchenko {
8925e111ed8SAndrew Rybchenko /* Read to end of partition */
8935e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
8945e111ed8SAndrew Rybchenko efx_rc_t rc;
8955e111ed8SAndrew Rybchenko uint32_t *segment_used;
8965e111ed8SAndrew Rybchenko
8975e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(offset))
8985e111ed8SAndrew Rybchenko
8995e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
9005e111ed8SAndrew Rybchenko buffer_size)) != 0) {
9015e111ed8SAndrew Rybchenko rc = EFAULT;
9025e111ed8SAndrew Rybchenko goto fail1;
9035e111ed8SAndrew Rybchenko }
9045e111ed8SAndrew Rybchenko
9055e111ed8SAndrew Rybchenko segment_used = cursor.block;
9065e111ed8SAndrew Rybchenko
9075e111ed8SAndrew Rybchenko /*
9085e111ed8SAndrew Rybchenko * Go through each segment and check that it has an end tag. If there
9095e111ed8SAndrew Rybchenko * is no end tag then the previous segment was the last valid one,
9105e111ed8SAndrew Rybchenko * so return the used space including that end tag.
9115e111ed8SAndrew Rybchenko */
9125e111ed8SAndrew Rybchenko while (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
9135e111ed8SAndrew Rybchenko if (tlv_require_end(&cursor) != 0) {
9145e111ed8SAndrew Rybchenko if (segment_used == cursor.block) {
9155e111ed8SAndrew Rybchenko /*
9165e111ed8SAndrew Rybchenko * First segment is corrupt, so there is
9175e111ed8SAndrew Rybchenko * no valid data in partition.
9185e111ed8SAndrew Rybchenko */
9195e111ed8SAndrew Rybchenko rc = EINVAL;
9205e111ed8SAndrew Rybchenko goto fail2;
9215e111ed8SAndrew Rybchenko }
9225e111ed8SAndrew Rybchenko break;
9235e111ed8SAndrew Rybchenko }
9245e111ed8SAndrew Rybchenko segment_used = cursor.end + 1;
9255e111ed8SAndrew Rybchenko
9265e111ed8SAndrew Rybchenko cursor.current = segment_used;
9275e111ed8SAndrew Rybchenko }
9285e111ed8SAndrew Rybchenko /* Return space used (including the END tag) */
9295e111ed8SAndrew Rybchenko *endp = (segment_used - cursor.block) * sizeof (uint32_t);
9305e111ed8SAndrew Rybchenko
9315e111ed8SAndrew Rybchenko return (0);
9325e111ed8SAndrew Rybchenko
9335e111ed8SAndrew Rybchenko fail2:
9345e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
9355e111ed8SAndrew Rybchenko fail1:
9365e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
9375e111ed8SAndrew Rybchenko
9385e111ed8SAndrew Rybchenko return (rc);
9395e111ed8SAndrew Rybchenko }
9405e111ed8SAndrew Rybchenko
9415e111ed8SAndrew Rybchenko __checkReturn __success(return != B_FALSE) boolean_t
ef10_nvram_buffer_find_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * startp,__out uint32_t * lengthp)9425e111ed8SAndrew Rybchenko ef10_nvram_buffer_find_item(
9435e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
9445e111ed8SAndrew Rybchenko caddr_t bufferp,
9455e111ed8SAndrew Rybchenko __in size_t buffer_size,
9465e111ed8SAndrew Rybchenko __in uint32_t offset,
9475e111ed8SAndrew Rybchenko __out uint32_t *startp,
9485e111ed8SAndrew Rybchenko __out uint32_t *lengthp)
9495e111ed8SAndrew Rybchenko {
9505e111ed8SAndrew Rybchenko /* Find TLV at offset and return key start and length */
9515e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
9525e111ed8SAndrew Rybchenko uint8_t *key;
9535e111ed8SAndrew Rybchenko uint32_t tag;
9545e111ed8SAndrew Rybchenko
9555e111ed8SAndrew Rybchenko if (tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
9565e111ed8SAndrew Rybchenko buffer_size, offset) != 0) {
9575e111ed8SAndrew Rybchenko return (B_FALSE);
9585e111ed8SAndrew Rybchenko }
9595e111ed8SAndrew Rybchenko
9605e111ed8SAndrew Rybchenko while ((key = tlv_item(&cursor)) != NULL) {
9615e111ed8SAndrew Rybchenko tag = tlv_tag(&cursor);
9625e111ed8SAndrew Rybchenko if (tag == TLV_TAG_PARTITION_HEADER ||
9635e111ed8SAndrew Rybchenko tag == TLV_TAG_PARTITION_TRAILER) {
9645e111ed8SAndrew Rybchenko if (tlv_advance(&cursor) != 0) {
9655e111ed8SAndrew Rybchenko break;
9665e111ed8SAndrew Rybchenko }
9675e111ed8SAndrew Rybchenko continue;
9685e111ed8SAndrew Rybchenko }
9695e111ed8SAndrew Rybchenko *startp = byte_offset(cursor.current, cursor.block);
9705e111ed8SAndrew Rybchenko *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
9715e111ed8SAndrew Rybchenko cursor.current);
9725e111ed8SAndrew Rybchenko return (B_TRUE);
9735e111ed8SAndrew Rybchenko }
9745e111ed8SAndrew Rybchenko
9755e111ed8SAndrew Rybchenko return (B_FALSE);
9765e111ed8SAndrew Rybchenko }
9775e111ed8SAndrew Rybchenko
9785e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_peek_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__out uint32_t * tagp,__out uint32_t * lengthp,__out uint32_t * value_offsetp)9795e111ed8SAndrew Rybchenko ef10_nvram_buffer_peek_item(
9805e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
9815e111ed8SAndrew Rybchenko caddr_t bufferp,
9825e111ed8SAndrew Rybchenko __in size_t buffer_size,
9835e111ed8SAndrew Rybchenko __in uint32_t offset,
9845e111ed8SAndrew Rybchenko __out uint32_t *tagp,
9855e111ed8SAndrew Rybchenko __out uint32_t *lengthp,
9865e111ed8SAndrew Rybchenko __out uint32_t *value_offsetp)
9875e111ed8SAndrew Rybchenko {
9885e111ed8SAndrew Rybchenko efx_rc_t rc;
9895e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
9905e111ed8SAndrew Rybchenko uint32_t tag;
9915e111ed8SAndrew Rybchenko
9925e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
9935e111ed8SAndrew Rybchenko buffer_size, offset)) != 0) {
9945e111ed8SAndrew Rybchenko goto fail1;
9955e111ed8SAndrew Rybchenko }
9965e111ed8SAndrew Rybchenko
9975e111ed8SAndrew Rybchenko tag = tlv_tag(&cursor);
9985e111ed8SAndrew Rybchenko *tagp = tag;
9995e111ed8SAndrew Rybchenko if (tag == TLV_TAG_END) {
10005e111ed8SAndrew Rybchenko /*
10015e111ed8SAndrew Rybchenko * To allow stepping over the END tag, report the full tag
10025e111ed8SAndrew Rybchenko * length and a zero length value.
10035e111ed8SAndrew Rybchenko */
10045e111ed8SAndrew Rybchenko *lengthp = sizeof (tag);
10055e111ed8SAndrew Rybchenko *value_offsetp = sizeof (tag);
10065e111ed8SAndrew Rybchenko } else {
10075e111ed8SAndrew Rybchenko *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
10085e111ed8SAndrew Rybchenko cursor.current);
10095e111ed8SAndrew Rybchenko *value_offsetp = byte_offset((uint32_t *)tlv_value(&cursor),
10105e111ed8SAndrew Rybchenko cursor.current);
10115e111ed8SAndrew Rybchenko }
10125e111ed8SAndrew Rybchenko return (0);
10135e111ed8SAndrew Rybchenko
10145e111ed8SAndrew Rybchenko fail1:
10155e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
10165e111ed8SAndrew Rybchenko
10175e111ed8SAndrew Rybchenko return (rc);
10185e111ed8SAndrew Rybchenko }
10195e111ed8SAndrew Rybchenko
10205e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_get_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__out uint32_t * tagp,__out_bcount_part (value_max_size,* lengthp)caddr_t valuep,__in size_t value_max_size,__out uint32_t * lengthp)10215e111ed8SAndrew Rybchenko ef10_nvram_buffer_get_item(
10225e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
10235e111ed8SAndrew Rybchenko caddr_t bufferp,
10245e111ed8SAndrew Rybchenko __in size_t buffer_size,
10255e111ed8SAndrew Rybchenko __in uint32_t offset,
10265e111ed8SAndrew Rybchenko __in uint32_t length,
10275e111ed8SAndrew Rybchenko __out uint32_t *tagp,
10285e111ed8SAndrew Rybchenko __out_bcount_part(value_max_size, *lengthp)
10295e111ed8SAndrew Rybchenko caddr_t valuep,
10305e111ed8SAndrew Rybchenko __in size_t value_max_size,
10315e111ed8SAndrew Rybchenko __out uint32_t *lengthp)
10325e111ed8SAndrew Rybchenko {
10335e111ed8SAndrew Rybchenko efx_rc_t rc;
10345e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
10355e111ed8SAndrew Rybchenko uint32_t value_length;
10365e111ed8SAndrew Rybchenko
10375e111ed8SAndrew Rybchenko if (buffer_size < (offset + length)) {
10385e111ed8SAndrew Rybchenko rc = ENOSPC;
10395e111ed8SAndrew Rybchenko goto fail1;
10405e111ed8SAndrew Rybchenko }
10415e111ed8SAndrew Rybchenko
10425e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
10435e111ed8SAndrew Rybchenko buffer_size, offset)) != 0) {
10445e111ed8SAndrew Rybchenko goto fail2;
10455e111ed8SAndrew Rybchenko }
10465e111ed8SAndrew Rybchenko
10475e111ed8SAndrew Rybchenko value_length = tlv_length(&cursor);
10485e111ed8SAndrew Rybchenko if (value_max_size < value_length) {
10495e111ed8SAndrew Rybchenko rc = ENOSPC;
10505e111ed8SAndrew Rybchenko goto fail3;
10515e111ed8SAndrew Rybchenko }
10525e111ed8SAndrew Rybchenko memcpy(valuep, tlv_value(&cursor), value_length);
10535e111ed8SAndrew Rybchenko
10545e111ed8SAndrew Rybchenko *tagp = tlv_tag(&cursor);
10555e111ed8SAndrew Rybchenko *lengthp = value_length;
10565e111ed8SAndrew Rybchenko
10575e111ed8SAndrew Rybchenko return (0);
10585e111ed8SAndrew Rybchenko
10595e111ed8SAndrew Rybchenko fail3:
10605e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
10615e111ed8SAndrew Rybchenko fail2:
10625e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
10635e111ed8SAndrew Rybchenko fail1:
10645e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
10655e111ed8SAndrew Rybchenko
10665e111ed8SAndrew Rybchenko return (rc);
10675e111ed8SAndrew Rybchenko }
10685e111ed8SAndrew Rybchenko
10695e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_insert_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t tag,__in_bcount (length)caddr_t valuep,__in uint32_t length,__out uint32_t * lengthp)10705e111ed8SAndrew Rybchenko ef10_nvram_buffer_insert_item(
10715e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
10725e111ed8SAndrew Rybchenko caddr_t bufferp,
10735e111ed8SAndrew Rybchenko __in size_t buffer_size,
10745e111ed8SAndrew Rybchenko __in uint32_t offset,
10755e111ed8SAndrew Rybchenko __in uint32_t tag,
10765e111ed8SAndrew Rybchenko __in_bcount(length) caddr_t valuep,
10775e111ed8SAndrew Rybchenko __in uint32_t length,
10785e111ed8SAndrew Rybchenko __out uint32_t *lengthp)
10795e111ed8SAndrew Rybchenko {
10805e111ed8SAndrew Rybchenko efx_rc_t rc;
10815e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
10825e111ed8SAndrew Rybchenko
10835e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
10845e111ed8SAndrew Rybchenko buffer_size, offset)) != 0) {
10855e111ed8SAndrew Rybchenko goto fail1;
10865e111ed8SAndrew Rybchenko }
10875e111ed8SAndrew Rybchenko
10885e111ed8SAndrew Rybchenko rc = tlv_insert(&cursor, tag, (uint8_t *)valuep, length);
10895e111ed8SAndrew Rybchenko
10905e111ed8SAndrew Rybchenko if (rc != 0)
10915e111ed8SAndrew Rybchenko goto fail2;
10925e111ed8SAndrew Rybchenko
10935e111ed8SAndrew Rybchenko *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
10945e111ed8SAndrew Rybchenko cursor.current);
10955e111ed8SAndrew Rybchenko
10965e111ed8SAndrew Rybchenko return (0);
10975e111ed8SAndrew Rybchenko
10985e111ed8SAndrew Rybchenko fail2:
10995e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
11005e111ed8SAndrew Rybchenko fail1:
11015e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
11025e111ed8SAndrew Rybchenko
11035e111ed8SAndrew Rybchenko return (rc);
11045e111ed8SAndrew Rybchenko }
11055e111ed8SAndrew Rybchenko
11065e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_modify_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t tag,__in_bcount (length)caddr_t valuep,__in uint32_t length,__out uint32_t * lengthp)11075e111ed8SAndrew Rybchenko ef10_nvram_buffer_modify_item(
11085e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
11095e111ed8SAndrew Rybchenko caddr_t bufferp,
11105e111ed8SAndrew Rybchenko __in size_t buffer_size,
11115e111ed8SAndrew Rybchenko __in uint32_t offset,
11125e111ed8SAndrew Rybchenko __in uint32_t tag,
11135e111ed8SAndrew Rybchenko __in_bcount(length) caddr_t valuep,
11145e111ed8SAndrew Rybchenko __in uint32_t length,
11155e111ed8SAndrew Rybchenko __out uint32_t *lengthp)
11165e111ed8SAndrew Rybchenko {
11175e111ed8SAndrew Rybchenko efx_rc_t rc;
11185e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
11195e111ed8SAndrew Rybchenko
11205e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11215e111ed8SAndrew Rybchenko buffer_size, offset)) != 0) {
11225e111ed8SAndrew Rybchenko goto fail1;
11235e111ed8SAndrew Rybchenko }
11245e111ed8SAndrew Rybchenko
11255e111ed8SAndrew Rybchenko rc = tlv_modify(&cursor, tag, (uint8_t *)valuep, length);
11265e111ed8SAndrew Rybchenko
11275e111ed8SAndrew Rybchenko if (rc != 0) {
11285e111ed8SAndrew Rybchenko goto fail2;
11295e111ed8SAndrew Rybchenko }
11305e111ed8SAndrew Rybchenko
11315e111ed8SAndrew Rybchenko *lengthp = byte_offset(tlv_next_item_ptr(&cursor),
11325e111ed8SAndrew Rybchenko cursor.current);
11335e111ed8SAndrew Rybchenko
11345e111ed8SAndrew Rybchenko return (0);
11355e111ed8SAndrew Rybchenko
11365e111ed8SAndrew Rybchenko fail2:
11375e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
11385e111ed8SAndrew Rybchenko fail1:
11395e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
11405e111ed8SAndrew Rybchenko
11415e111ed8SAndrew Rybchenko return (rc);
11425e111ed8SAndrew Rybchenko }
11435e111ed8SAndrew Rybchenko
11445e111ed8SAndrew Rybchenko
11455e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_delete_item(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size,__in uint32_t offset,__in uint32_t length,__in uint32_t end)11465e111ed8SAndrew Rybchenko ef10_nvram_buffer_delete_item(
11475e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
11485e111ed8SAndrew Rybchenko caddr_t bufferp,
11495e111ed8SAndrew Rybchenko __in size_t buffer_size,
11505e111ed8SAndrew Rybchenko __in uint32_t offset,
11515e111ed8SAndrew Rybchenko __in uint32_t length,
11525e111ed8SAndrew Rybchenko __in uint32_t end)
11535e111ed8SAndrew Rybchenko {
11545e111ed8SAndrew Rybchenko efx_rc_t rc;
11555e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
11565e111ed8SAndrew Rybchenko
11575e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(length, end))
11585e111ed8SAndrew Rybchenko
11595e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_at_offset(&cursor, (uint8_t *)bufferp,
11605e111ed8SAndrew Rybchenko buffer_size, offset)) != 0) {
11615e111ed8SAndrew Rybchenko goto fail1;
11625e111ed8SAndrew Rybchenko }
11635e111ed8SAndrew Rybchenko
11645e111ed8SAndrew Rybchenko if ((rc = tlv_delete(&cursor)) != 0)
11655e111ed8SAndrew Rybchenko goto fail2;
11665e111ed8SAndrew Rybchenko
11675e111ed8SAndrew Rybchenko return (0);
11685e111ed8SAndrew Rybchenko
11695e111ed8SAndrew Rybchenko fail2:
11705e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
11715e111ed8SAndrew Rybchenko fail1:
11725e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
11735e111ed8SAndrew Rybchenko
11745e111ed8SAndrew Rybchenko return (rc);
11755e111ed8SAndrew Rybchenko }
11765e111ed8SAndrew Rybchenko
11775e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buffer_finish(__in_bcount (buffer_size)caddr_t bufferp,__in size_t buffer_size)11785e111ed8SAndrew Rybchenko ef10_nvram_buffer_finish(
11795e111ed8SAndrew Rybchenko __in_bcount(buffer_size)
11805e111ed8SAndrew Rybchenko caddr_t bufferp,
11815e111ed8SAndrew Rybchenko __in size_t buffer_size)
11825e111ed8SAndrew Rybchenko {
11835e111ed8SAndrew Rybchenko efx_rc_t rc;
11845e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
11855e111ed8SAndrew Rybchenko
11865e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)bufferp,
11875e111ed8SAndrew Rybchenko buffer_size)) != 0) {
11885e111ed8SAndrew Rybchenko rc = EFAULT;
11895e111ed8SAndrew Rybchenko goto fail1;
11905e111ed8SAndrew Rybchenko }
11915e111ed8SAndrew Rybchenko
11925e111ed8SAndrew Rybchenko if ((rc = tlv_require_end(&cursor)) != 0)
11935e111ed8SAndrew Rybchenko goto fail2;
11945e111ed8SAndrew Rybchenko
11955e111ed8SAndrew Rybchenko if ((rc = tlv_update_partition_len_and_cks(&cursor)) != 0)
11965e111ed8SAndrew Rybchenko goto fail3;
11975e111ed8SAndrew Rybchenko
11985e111ed8SAndrew Rybchenko return (0);
11995e111ed8SAndrew Rybchenko
12005e111ed8SAndrew Rybchenko fail3:
12015e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
12025e111ed8SAndrew Rybchenko fail2:
12035e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
12045e111ed8SAndrew Rybchenko fail1:
12055e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
12065e111ed8SAndrew Rybchenko
12075e111ed8SAndrew Rybchenko return (rc);
12085e111ed8SAndrew Rybchenko }
12095e111ed8SAndrew Rybchenko
12105e111ed8SAndrew Rybchenko
12115e111ed8SAndrew Rybchenko
12125e111ed8SAndrew Rybchenko /*
12135e111ed8SAndrew Rybchenko * Read and validate a segment from a partition. A segment is a complete
12145e111ed8SAndrew Rybchenko * tlv chain between PARTITION_HEADER and PARTITION_END tags. There may
12155e111ed8SAndrew Rybchenko * be multiple segments in a partition, so seg_offset allows segments
12165e111ed8SAndrew Rybchenko * beyond the first to be read.
12175e111ed8SAndrew Rybchenko */
12185e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
ef10_nvram_read_tlv_segment(__in efx_nic_t * enp,__in uint32_t partn,__in size_t seg_offset,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size)12195e111ed8SAndrew Rybchenko ef10_nvram_read_tlv_segment(
12205e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
12215e111ed8SAndrew Rybchenko __in uint32_t partn,
12225e111ed8SAndrew Rybchenko __in size_t seg_offset,
12235e111ed8SAndrew Rybchenko __in_bcount(max_seg_size) caddr_t seg_data,
12245e111ed8SAndrew Rybchenko __in size_t max_seg_size)
12255e111ed8SAndrew Rybchenko {
12265e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
12275e111ed8SAndrew Rybchenko struct tlv_partition_header *header;
12285e111ed8SAndrew Rybchenko struct tlv_partition_trailer *trailer;
12295e111ed8SAndrew Rybchenko size_t total_length;
12305e111ed8SAndrew Rybchenko uint32_t cksum;
12315e111ed8SAndrew Rybchenko int pos;
12325e111ed8SAndrew Rybchenko efx_rc_t rc;
12335e111ed8SAndrew Rybchenko
12345e111ed8SAndrew Rybchenko EFX_STATIC_ASSERT(sizeof (*header) <= EF10_NVRAM_CHUNK);
12355e111ed8SAndrew Rybchenko
12365e111ed8SAndrew Rybchenko if ((seg_data == NULL) || (max_seg_size == 0)) {
12375e111ed8SAndrew Rybchenko rc = EINVAL;
12385e111ed8SAndrew Rybchenko goto fail1;
12395e111ed8SAndrew Rybchenko }
12405e111ed8SAndrew Rybchenko
12415e111ed8SAndrew Rybchenko /* Read initial chunk of the segment, starting at offset */
12425e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_read_mode(enp, partn, seg_offset, seg_data,
12435e111ed8SAndrew Rybchenko EF10_NVRAM_CHUNK,
12445e111ed8SAndrew Rybchenko MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0) {
12455e111ed8SAndrew Rybchenko goto fail2;
12465e111ed8SAndrew Rybchenko }
12475e111ed8SAndrew Rybchenko
12485e111ed8SAndrew Rybchenko /* A PARTITION_HEADER tag must be the first item at the given offset */
12495e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
12505e111ed8SAndrew Rybchenko max_seg_size)) != 0) {
12515e111ed8SAndrew Rybchenko rc = EFAULT;
12525e111ed8SAndrew Rybchenko goto fail3;
12535e111ed8SAndrew Rybchenko }
12545e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
12555e111ed8SAndrew Rybchenko rc = EINVAL;
12565e111ed8SAndrew Rybchenko goto fail4;
12575e111ed8SAndrew Rybchenko }
12585e111ed8SAndrew Rybchenko header = (struct tlv_partition_header *)tlv_item(&cursor);
12595e111ed8SAndrew Rybchenko
12605e111ed8SAndrew Rybchenko /* Check TLV segment length (includes the END tag) */
12615e111ed8SAndrew Rybchenko total_length = __LE_TO_CPU_32(header->total_length);
12625e111ed8SAndrew Rybchenko if (total_length > max_seg_size) {
12635e111ed8SAndrew Rybchenko rc = EFBIG;
12645e111ed8SAndrew Rybchenko goto fail5;
12655e111ed8SAndrew Rybchenko }
12665e111ed8SAndrew Rybchenko
12675e111ed8SAndrew Rybchenko /* Read the remaining segment content */
12685e111ed8SAndrew Rybchenko if (total_length > EF10_NVRAM_CHUNK) {
12695e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_read_mode(enp, partn,
12705e111ed8SAndrew Rybchenko seg_offset + EF10_NVRAM_CHUNK,
12715e111ed8SAndrew Rybchenko seg_data + EF10_NVRAM_CHUNK,
12725e111ed8SAndrew Rybchenko total_length - EF10_NVRAM_CHUNK,
12735e111ed8SAndrew Rybchenko MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT)) != 0)
12745e111ed8SAndrew Rybchenko goto fail6;
12755e111ed8SAndrew Rybchenko }
12765e111ed8SAndrew Rybchenko
12775e111ed8SAndrew Rybchenko /* Check segment ends with PARTITION_TRAILER and END tags */
12785e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
12795e111ed8SAndrew Rybchenko rc = EINVAL;
12805e111ed8SAndrew Rybchenko goto fail7;
12815e111ed8SAndrew Rybchenko }
12825e111ed8SAndrew Rybchenko trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
12835e111ed8SAndrew Rybchenko
12845e111ed8SAndrew Rybchenko if ((rc = tlv_advance(&cursor)) != 0) {
12855e111ed8SAndrew Rybchenko rc = EINVAL;
12865e111ed8SAndrew Rybchenko goto fail8;
12875e111ed8SAndrew Rybchenko }
12885e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_END) {
12895e111ed8SAndrew Rybchenko rc = EINVAL;
12905e111ed8SAndrew Rybchenko goto fail9;
12915e111ed8SAndrew Rybchenko }
12925e111ed8SAndrew Rybchenko
12935e111ed8SAndrew Rybchenko /* Check data read from segment is consistent */
12945e111ed8SAndrew Rybchenko if (trailer->generation != header->generation) {
12955e111ed8SAndrew Rybchenko /*
12965e111ed8SAndrew Rybchenko * The partition data may have been modified between successive
12975e111ed8SAndrew Rybchenko * MCDI NVRAM_READ requests by the MC or another PCI function.
12985e111ed8SAndrew Rybchenko *
12995e111ed8SAndrew Rybchenko * The caller must retry to obtain consistent partition data.
13005e111ed8SAndrew Rybchenko */
13015e111ed8SAndrew Rybchenko rc = EAGAIN;
13025e111ed8SAndrew Rybchenko goto fail10;
13035e111ed8SAndrew Rybchenko }
13045e111ed8SAndrew Rybchenko
13055e111ed8SAndrew Rybchenko /* Verify segment checksum */
13065e111ed8SAndrew Rybchenko cksum = 0;
13075e111ed8SAndrew Rybchenko for (pos = 0; (size_t)pos < total_length; pos += sizeof (uint32_t)) {
13085e111ed8SAndrew Rybchenko cksum += *((uint32_t *)(seg_data + pos));
13095e111ed8SAndrew Rybchenko }
13105e111ed8SAndrew Rybchenko if (cksum != 0) {
13115e111ed8SAndrew Rybchenko rc = EINVAL;
13125e111ed8SAndrew Rybchenko goto fail11;
13135e111ed8SAndrew Rybchenko }
13145e111ed8SAndrew Rybchenko
13155e111ed8SAndrew Rybchenko return (0);
13165e111ed8SAndrew Rybchenko
13175e111ed8SAndrew Rybchenko fail11:
13185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail11);
13195e111ed8SAndrew Rybchenko fail10:
13205e111ed8SAndrew Rybchenko EFSYS_PROBE(fail10);
13215e111ed8SAndrew Rybchenko fail9:
13225e111ed8SAndrew Rybchenko EFSYS_PROBE(fail9);
13235e111ed8SAndrew Rybchenko fail8:
13245e111ed8SAndrew Rybchenko EFSYS_PROBE(fail8);
13255e111ed8SAndrew Rybchenko fail7:
13265e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
13275e111ed8SAndrew Rybchenko fail6:
13285e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
13295e111ed8SAndrew Rybchenko fail5:
13305e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
13315e111ed8SAndrew Rybchenko fail4:
13325e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
13335e111ed8SAndrew Rybchenko fail3:
13345e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
13355e111ed8SAndrew Rybchenko fail2:
13365e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
13375e111ed8SAndrew Rybchenko fail1:
13385e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
13395e111ed8SAndrew Rybchenko
13405e111ed8SAndrew Rybchenko return (rc);
13415e111ed8SAndrew Rybchenko }
13425e111ed8SAndrew Rybchenko
13435e111ed8SAndrew Rybchenko /*
13445e111ed8SAndrew Rybchenko * Read a single TLV item from a host memory
13455e111ed8SAndrew Rybchenko * buffer containing a TLV formatted segment.
13465e111ed8SAndrew Rybchenko */
13475e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buf_read_tlv(__in efx_nic_t * enp,__in_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__deref_out_bcount_opt (* sizep)caddr_t * datap,__out size_t * sizep)13485e111ed8SAndrew Rybchenko ef10_nvram_buf_read_tlv(
13495e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
13505e111ed8SAndrew Rybchenko __in_bcount(max_seg_size) caddr_t seg_data,
13515e111ed8SAndrew Rybchenko __in size_t max_seg_size,
13525e111ed8SAndrew Rybchenko __in uint32_t tag,
13535e111ed8SAndrew Rybchenko __deref_out_bcount_opt(*sizep) caddr_t *datap,
13545e111ed8SAndrew Rybchenko __out size_t *sizep)
13555e111ed8SAndrew Rybchenko {
13565e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
13575e111ed8SAndrew Rybchenko caddr_t data;
13585e111ed8SAndrew Rybchenko size_t length;
13595e111ed8SAndrew Rybchenko caddr_t value;
13605e111ed8SAndrew Rybchenko efx_rc_t rc;
13615e111ed8SAndrew Rybchenko
13625e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(enp))
13635e111ed8SAndrew Rybchenko
13645e111ed8SAndrew Rybchenko if ((seg_data == NULL) || (max_seg_size == 0)) {
13655e111ed8SAndrew Rybchenko rc = EINVAL;
13665e111ed8SAndrew Rybchenko goto fail1;
13675e111ed8SAndrew Rybchenko }
13685e111ed8SAndrew Rybchenko
13695e111ed8SAndrew Rybchenko /* Find requested TLV tag in segment data */
13705e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
13715e111ed8SAndrew Rybchenko max_seg_size)) != 0) {
13725e111ed8SAndrew Rybchenko rc = EFAULT;
13735e111ed8SAndrew Rybchenko goto fail2;
13745e111ed8SAndrew Rybchenko }
13755e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, tag)) != 0) {
13765e111ed8SAndrew Rybchenko rc = ENOENT;
13775e111ed8SAndrew Rybchenko goto fail3;
13785e111ed8SAndrew Rybchenko }
13795e111ed8SAndrew Rybchenko value = (caddr_t)tlv_value(&cursor);
13805e111ed8SAndrew Rybchenko length = tlv_length(&cursor);
13815e111ed8SAndrew Rybchenko
13825e111ed8SAndrew Rybchenko if (length == 0)
13835e111ed8SAndrew Rybchenko data = NULL;
13845e111ed8SAndrew Rybchenko else {
13855e111ed8SAndrew Rybchenko /* Copy out data from TLV item */
13865e111ed8SAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, length, data);
13875e111ed8SAndrew Rybchenko if (data == NULL) {
13885e111ed8SAndrew Rybchenko rc = ENOMEM;
13895e111ed8SAndrew Rybchenko goto fail4;
13905e111ed8SAndrew Rybchenko }
13915e111ed8SAndrew Rybchenko memcpy(data, value, length);
13925e111ed8SAndrew Rybchenko }
13935e111ed8SAndrew Rybchenko
13945e111ed8SAndrew Rybchenko *datap = data;
13955e111ed8SAndrew Rybchenko *sizep = length;
13965e111ed8SAndrew Rybchenko
13975e111ed8SAndrew Rybchenko return (0);
13985e111ed8SAndrew Rybchenko
13995e111ed8SAndrew Rybchenko fail4:
14005e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
14015e111ed8SAndrew Rybchenko fail3:
14025e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
14035e111ed8SAndrew Rybchenko fail2:
14045e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
14055e111ed8SAndrew Rybchenko fail1:
14065e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
14075e111ed8SAndrew Rybchenko
14085e111ed8SAndrew Rybchenko return (rc);
14095e111ed8SAndrew Rybchenko }
14105e111ed8SAndrew Rybchenko
14115e111ed8SAndrew Rybchenko /* Read a single TLV item from the first segment in a TLV formatted partition */
14125e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_read_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__deref_out_bcount_opt (* seg_sizep)caddr_t * seg_datap,__out size_t * seg_sizep)14135e111ed8SAndrew Rybchenko ef10_nvram_partn_read_tlv(
14145e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
14155e111ed8SAndrew Rybchenko __in uint32_t partn,
14165e111ed8SAndrew Rybchenko __in uint32_t tag,
14175e111ed8SAndrew Rybchenko __deref_out_bcount_opt(*seg_sizep) caddr_t *seg_datap,
14185e111ed8SAndrew Rybchenko __out size_t *seg_sizep)
14195e111ed8SAndrew Rybchenko {
14205e111ed8SAndrew Rybchenko caddr_t seg_data = NULL;
14215e111ed8SAndrew Rybchenko size_t partn_size = 0;
14225e111ed8SAndrew Rybchenko size_t length;
14235e111ed8SAndrew Rybchenko caddr_t data;
14245e111ed8SAndrew Rybchenko int retry;
14255e111ed8SAndrew Rybchenko efx_rc_t rc;
14265e111ed8SAndrew Rybchenko
14275e111ed8SAndrew Rybchenko /* Allocate sufficient memory for the entire partition */
14285e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
14295e111ed8SAndrew Rybchenko goto fail1;
14305e111ed8SAndrew Rybchenko
14315e111ed8SAndrew Rybchenko if (partn_size == 0) {
14325e111ed8SAndrew Rybchenko rc = ENOENT;
14335e111ed8SAndrew Rybchenko goto fail2;
14345e111ed8SAndrew Rybchenko }
14355e111ed8SAndrew Rybchenko
14365e111ed8SAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, seg_data);
14375e111ed8SAndrew Rybchenko if (seg_data == NULL) {
14385e111ed8SAndrew Rybchenko rc = ENOMEM;
14395e111ed8SAndrew Rybchenko goto fail3;
14405e111ed8SAndrew Rybchenko }
14415e111ed8SAndrew Rybchenko
14425e111ed8SAndrew Rybchenko /*
14435e111ed8SAndrew Rybchenko * Read the first segment in a TLV partition. Retry until consistent
14445e111ed8SAndrew Rybchenko * segment contents are returned. Inconsistent data may be read if:
14455e111ed8SAndrew Rybchenko * a) the segment contents are invalid
14465e111ed8SAndrew Rybchenko * b) the MC has rebooted while we were reading the partition
14475e111ed8SAndrew Rybchenko * c) the partition has been modified while we were reading it
14485e111ed8SAndrew Rybchenko * Limit retry attempts to ensure forward progress.
14495e111ed8SAndrew Rybchenko */
14505e111ed8SAndrew Rybchenko retry = 10;
14515e111ed8SAndrew Rybchenko do {
14525e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_read_tlv_segment(enp, partn, 0,
14535e111ed8SAndrew Rybchenko seg_data, partn_size)) != 0)
14545e111ed8SAndrew Rybchenko --retry;
14555e111ed8SAndrew Rybchenko } while ((rc == EAGAIN) && (retry > 0));
14565e111ed8SAndrew Rybchenko
14575e111ed8SAndrew Rybchenko if (rc != 0) {
14585e111ed8SAndrew Rybchenko /* Failed to obtain consistent segment data */
14595e111ed8SAndrew Rybchenko if (rc == EAGAIN)
14605e111ed8SAndrew Rybchenko rc = EIO;
14615e111ed8SAndrew Rybchenko
14625e111ed8SAndrew Rybchenko goto fail4;
14635e111ed8SAndrew Rybchenko }
14645e111ed8SAndrew Rybchenko
14655e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_buf_read_tlv(enp, seg_data, partn_size,
14665e111ed8SAndrew Rybchenko tag, &data, &length)) != 0)
14675e111ed8SAndrew Rybchenko goto fail5;
14685e111ed8SAndrew Rybchenko
14695e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
14705e111ed8SAndrew Rybchenko
14715e111ed8SAndrew Rybchenko *seg_datap = data;
14725e111ed8SAndrew Rybchenko *seg_sizep = length;
14735e111ed8SAndrew Rybchenko
14745e111ed8SAndrew Rybchenko return (0);
14755e111ed8SAndrew Rybchenko
14765e111ed8SAndrew Rybchenko fail5:
14775e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
14785e111ed8SAndrew Rybchenko fail4:
14795e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
14805e111ed8SAndrew Rybchenko
14815e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_size, seg_data);
14825e111ed8SAndrew Rybchenko fail3:
14835e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
14845e111ed8SAndrew Rybchenko fail2:
14855e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
14865e111ed8SAndrew Rybchenko fail1:
14875e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
14885e111ed8SAndrew Rybchenko
14895e111ed8SAndrew Rybchenko return (rc);
14905e111ed8SAndrew Rybchenko }
14915e111ed8SAndrew Rybchenko
14925e111ed8SAndrew Rybchenko /* Compute the size of a segment. */
14935e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
ef10_nvram_buf_segment_size(__in caddr_t seg_data,__in size_t max_seg_size,__out size_t * seg_sizep)14945e111ed8SAndrew Rybchenko ef10_nvram_buf_segment_size(
14955e111ed8SAndrew Rybchenko __in caddr_t seg_data,
14965e111ed8SAndrew Rybchenko __in size_t max_seg_size,
14975e111ed8SAndrew Rybchenko __out size_t *seg_sizep)
14985e111ed8SAndrew Rybchenko {
14995e111ed8SAndrew Rybchenko efx_rc_t rc;
15005e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
15015e111ed8SAndrew Rybchenko struct tlv_partition_header *header;
15025e111ed8SAndrew Rybchenko uint32_t cksum;
15035e111ed8SAndrew Rybchenko int pos;
15045e111ed8SAndrew Rybchenko uint32_t *end_tag_position;
15055e111ed8SAndrew Rybchenko uint32_t segment_length;
15065e111ed8SAndrew Rybchenko
15075e111ed8SAndrew Rybchenko /* A PARTITION_HEADER tag must be the first item at the given offset */
15085e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
15095e111ed8SAndrew Rybchenko max_seg_size)) != 0) {
15105e111ed8SAndrew Rybchenko rc = EFAULT;
15115e111ed8SAndrew Rybchenko goto fail1;
15125e111ed8SAndrew Rybchenko }
15135e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
15145e111ed8SAndrew Rybchenko rc = EINVAL;
15155e111ed8SAndrew Rybchenko goto fail2;
15165e111ed8SAndrew Rybchenko }
15175e111ed8SAndrew Rybchenko header = (struct tlv_partition_header *)tlv_item(&cursor);
15185e111ed8SAndrew Rybchenko
15195e111ed8SAndrew Rybchenko /* Check TLV segment length (includes the END tag) */
15205e111ed8SAndrew Rybchenko *seg_sizep = __LE_TO_CPU_32(header->total_length);
15215e111ed8SAndrew Rybchenko if (*seg_sizep > max_seg_size) {
15225e111ed8SAndrew Rybchenko rc = EFBIG;
15235e111ed8SAndrew Rybchenko goto fail3;
15245e111ed8SAndrew Rybchenko }
15255e111ed8SAndrew Rybchenko
15265e111ed8SAndrew Rybchenko /* Check segment ends with PARTITION_TRAILER and END tags */
15275e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
15285e111ed8SAndrew Rybchenko rc = EINVAL;
15295e111ed8SAndrew Rybchenko goto fail4;
15305e111ed8SAndrew Rybchenko }
15315e111ed8SAndrew Rybchenko
15325e111ed8SAndrew Rybchenko if ((rc = tlv_advance(&cursor)) != 0) {
15335e111ed8SAndrew Rybchenko rc = EINVAL;
15345e111ed8SAndrew Rybchenko goto fail5;
15355e111ed8SAndrew Rybchenko }
15365e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_END) {
15375e111ed8SAndrew Rybchenko rc = EINVAL;
15385e111ed8SAndrew Rybchenko goto fail6;
15395e111ed8SAndrew Rybchenko }
15405e111ed8SAndrew Rybchenko end_tag_position = cursor.current;
15415e111ed8SAndrew Rybchenko
15425e111ed8SAndrew Rybchenko /* Verify segment checksum */
15435e111ed8SAndrew Rybchenko cksum = 0;
15445e111ed8SAndrew Rybchenko for (pos = 0; (size_t)pos < *seg_sizep; pos += sizeof (uint32_t)) {
15455e111ed8SAndrew Rybchenko cksum += *((uint32_t *)(seg_data + pos));
15465e111ed8SAndrew Rybchenko }
15475e111ed8SAndrew Rybchenko if (cksum != 0) {
15485e111ed8SAndrew Rybchenko rc = EINVAL;
15495e111ed8SAndrew Rybchenko goto fail7;
15505e111ed8SAndrew Rybchenko }
15515e111ed8SAndrew Rybchenko
15525e111ed8SAndrew Rybchenko /*
15535e111ed8SAndrew Rybchenko * Calculate total length from HEADER to END tags and compare to
15545e111ed8SAndrew Rybchenko * max_seg_size and the total_length field in the HEADER tag.
15555e111ed8SAndrew Rybchenko */
15565e111ed8SAndrew Rybchenko segment_length = tlv_block_length_used(&cursor);
15575e111ed8SAndrew Rybchenko
15585e111ed8SAndrew Rybchenko if (segment_length > max_seg_size) {
15595e111ed8SAndrew Rybchenko rc = EINVAL;
15605e111ed8SAndrew Rybchenko goto fail8;
15615e111ed8SAndrew Rybchenko }
15625e111ed8SAndrew Rybchenko
15635e111ed8SAndrew Rybchenko if (segment_length != *seg_sizep) {
15645e111ed8SAndrew Rybchenko rc = EINVAL;
15655e111ed8SAndrew Rybchenko goto fail9;
15665e111ed8SAndrew Rybchenko }
15675e111ed8SAndrew Rybchenko
15685e111ed8SAndrew Rybchenko /* Skip over the first HEADER tag. */
15695e111ed8SAndrew Rybchenko rc = tlv_rewind(&cursor);
15705e111ed8SAndrew Rybchenko rc = tlv_advance(&cursor);
15715e111ed8SAndrew Rybchenko
15725e111ed8SAndrew Rybchenko while (rc == 0) {
15735e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) == TLV_TAG_END) {
15745e111ed8SAndrew Rybchenko /* Check that the END tag is the one found earlier. */
15755e111ed8SAndrew Rybchenko if (cursor.current != end_tag_position)
15765e111ed8SAndrew Rybchenko goto fail10;
15775e111ed8SAndrew Rybchenko break;
15785e111ed8SAndrew Rybchenko }
15795e111ed8SAndrew Rybchenko /* Check for duplicate HEADER tags before the END tag. */
15805e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) == TLV_TAG_PARTITION_HEADER) {
15815e111ed8SAndrew Rybchenko rc = EINVAL;
15825e111ed8SAndrew Rybchenko goto fail11;
15835e111ed8SAndrew Rybchenko }
15845e111ed8SAndrew Rybchenko
15855e111ed8SAndrew Rybchenko rc = tlv_advance(&cursor);
15865e111ed8SAndrew Rybchenko }
15875e111ed8SAndrew Rybchenko if (rc != 0)
15885e111ed8SAndrew Rybchenko goto fail12;
15895e111ed8SAndrew Rybchenko
15905e111ed8SAndrew Rybchenko return (0);
15915e111ed8SAndrew Rybchenko
15925e111ed8SAndrew Rybchenko fail12:
15935e111ed8SAndrew Rybchenko EFSYS_PROBE(fail12);
15945e111ed8SAndrew Rybchenko fail11:
15955e111ed8SAndrew Rybchenko EFSYS_PROBE(fail11);
15965e111ed8SAndrew Rybchenko fail10:
15975e111ed8SAndrew Rybchenko EFSYS_PROBE(fail10);
15985e111ed8SAndrew Rybchenko fail9:
15995e111ed8SAndrew Rybchenko EFSYS_PROBE(fail9);
16005e111ed8SAndrew Rybchenko fail8:
16015e111ed8SAndrew Rybchenko EFSYS_PROBE(fail8);
16025e111ed8SAndrew Rybchenko fail7:
16035e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
16045e111ed8SAndrew Rybchenko fail6:
16055e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
16065e111ed8SAndrew Rybchenko fail5:
16075e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
16085e111ed8SAndrew Rybchenko fail4:
16095e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
16105e111ed8SAndrew Rybchenko fail3:
16115e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
16125e111ed8SAndrew Rybchenko fail2:
16135e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
16145e111ed8SAndrew Rybchenko fail1:
16155e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
16165e111ed8SAndrew Rybchenko
16175e111ed8SAndrew Rybchenko return (rc);
16185e111ed8SAndrew Rybchenko }
16195e111ed8SAndrew Rybchenko
16205e111ed8SAndrew Rybchenko /*
16215e111ed8SAndrew Rybchenko * Add or update a single TLV item in a host memory buffer containing a TLV
16225e111ed8SAndrew Rybchenko * formatted segment. Historically partitions consisted of only one segment.
16235e111ed8SAndrew Rybchenko */
16245e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_buf_write_tlv(__inout_bcount (max_seg_size)caddr_t seg_data,__in size_t max_seg_size,__in uint32_t tag,__in_bcount (tag_size)caddr_t tag_data,__in size_t tag_size,__out size_t * total_lengthp)16255e111ed8SAndrew Rybchenko ef10_nvram_buf_write_tlv(
16265e111ed8SAndrew Rybchenko __inout_bcount(max_seg_size) caddr_t seg_data,
16275e111ed8SAndrew Rybchenko __in size_t max_seg_size,
16285e111ed8SAndrew Rybchenko __in uint32_t tag,
16295e111ed8SAndrew Rybchenko __in_bcount(tag_size) caddr_t tag_data,
16305e111ed8SAndrew Rybchenko __in size_t tag_size,
16315e111ed8SAndrew Rybchenko __out size_t *total_lengthp)
16325e111ed8SAndrew Rybchenko {
16335e111ed8SAndrew Rybchenko tlv_cursor_t cursor;
16345e111ed8SAndrew Rybchenko struct tlv_partition_header *header;
16355e111ed8SAndrew Rybchenko struct tlv_partition_trailer *trailer;
16365e111ed8SAndrew Rybchenko uint32_t generation;
16375e111ed8SAndrew Rybchenko uint32_t cksum;
16385e111ed8SAndrew Rybchenko int pos;
16395e111ed8SAndrew Rybchenko efx_rc_t rc;
16405e111ed8SAndrew Rybchenko
16415e111ed8SAndrew Rybchenko /* A PARTITION_HEADER tag must be the first item (at offset zero) */
16425e111ed8SAndrew Rybchenko if ((rc = tlv_init_cursor_from_size(&cursor, (uint8_t *)seg_data,
16435e111ed8SAndrew Rybchenko max_seg_size)) != 0) {
16445e111ed8SAndrew Rybchenko rc = EFAULT;
16455e111ed8SAndrew Rybchenko goto fail1;
16465e111ed8SAndrew Rybchenko }
16475e111ed8SAndrew Rybchenko if (tlv_tag(&cursor) != TLV_TAG_PARTITION_HEADER) {
16485e111ed8SAndrew Rybchenko rc = EINVAL;
16495e111ed8SAndrew Rybchenko goto fail2;
16505e111ed8SAndrew Rybchenko }
16515e111ed8SAndrew Rybchenko header = (struct tlv_partition_header *)tlv_item(&cursor);
16525e111ed8SAndrew Rybchenko
16535e111ed8SAndrew Rybchenko /* Update the TLV chain to contain the new data */
16545e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, tag)) == 0) {
16555e111ed8SAndrew Rybchenko /* Modify existing TLV item */
16565e111ed8SAndrew Rybchenko if ((rc = tlv_modify(&cursor, tag,
16575e111ed8SAndrew Rybchenko (uint8_t *)tag_data, tag_size)) != 0)
16585e111ed8SAndrew Rybchenko goto fail3;
16595e111ed8SAndrew Rybchenko } else {
16605e111ed8SAndrew Rybchenko /* Insert a new TLV item before the PARTITION_TRAILER */
16615e111ed8SAndrew Rybchenko rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER);
16625e111ed8SAndrew Rybchenko if (rc != 0) {
16635e111ed8SAndrew Rybchenko rc = EINVAL;
16645e111ed8SAndrew Rybchenko goto fail4;
16655e111ed8SAndrew Rybchenko }
16665e111ed8SAndrew Rybchenko if ((rc = tlv_insert(&cursor, tag,
16675e111ed8SAndrew Rybchenko (uint8_t *)tag_data, tag_size)) != 0) {
16685e111ed8SAndrew Rybchenko rc = EINVAL;
16695e111ed8SAndrew Rybchenko goto fail5;
16705e111ed8SAndrew Rybchenko }
16715e111ed8SAndrew Rybchenko }
16725e111ed8SAndrew Rybchenko
16735e111ed8SAndrew Rybchenko /* Find the trailer tag */
16745e111ed8SAndrew Rybchenko if ((rc = tlv_find(&cursor, TLV_TAG_PARTITION_TRAILER)) != 0) {
16755e111ed8SAndrew Rybchenko rc = EINVAL;
16765e111ed8SAndrew Rybchenko goto fail6;
16775e111ed8SAndrew Rybchenko }
16785e111ed8SAndrew Rybchenko trailer = (struct tlv_partition_trailer *)tlv_item(&cursor);
16795e111ed8SAndrew Rybchenko
16805e111ed8SAndrew Rybchenko /* Update PARTITION_HEADER and PARTITION_TRAILER fields */
16815e111ed8SAndrew Rybchenko *total_lengthp = tlv_block_length_used(&cursor);
16825e111ed8SAndrew Rybchenko if (*total_lengthp > max_seg_size) {
16835e111ed8SAndrew Rybchenko rc = ENOSPC;
16845e111ed8SAndrew Rybchenko goto fail7;
16855e111ed8SAndrew Rybchenko }
16865e111ed8SAndrew Rybchenko generation = __LE_TO_CPU_32(header->generation) + 1;
16875e111ed8SAndrew Rybchenko
16885e111ed8SAndrew Rybchenko header->total_length = __CPU_TO_LE_32(*total_lengthp);
16895e111ed8SAndrew Rybchenko header->generation = __CPU_TO_LE_32(generation);
16905e111ed8SAndrew Rybchenko trailer->generation = __CPU_TO_LE_32(generation);
16915e111ed8SAndrew Rybchenko
16925e111ed8SAndrew Rybchenko /* Recompute PARTITION_TRAILER checksum */
16935e111ed8SAndrew Rybchenko trailer->checksum = 0;
16945e111ed8SAndrew Rybchenko cksum = 0;
16955e111ed8SAndrew Rybchenko for (pos = 0; (size_t)pos < *total_lengthp; pos += sizeof (uint32_t)) {
16965e111ed8SAndrew Rybchenko cksum += *((uint32_t *)(seg_data + pos));
16975e111ed8SAndrew Rybchenko }
16985e111ed8SAndrew Rybchenko trailer->checksum = ~cksum + 1;
16995e111ed8SAndrew Rybchenko
17005e111ed8SAndrew Rybchenko return (0);
17015e111ed8SAndrew Rybchenko
17025e111ed8SAndrew Rybchenko fail7:
17035e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
17045e111ed8SAndrew Rybchenko fail6:
17055e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
17065e111ed8SAndrew Rybchenko fail5:
17075e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
17085e111ed8SAndrew Rybchenko fail4:
17095e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
17105e111ed8SAndrew Rybchenko fail3:
17115e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
17125e111ed8SAndrew Rybchenko fail2:
17135e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
17145e111ed8SAndrew Rybchenko fail1:
17155e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
17165e111ed8SAndrew Rybchenko
17175e111ed8SAndrew Rybchenko return (rc);
17185e111ed8SAndrew Rybchenko }
17195e111ed8SAndrew Rybchenko
17205e111ed8SAndrew Rybchenko /*
17215e111ed8SAndrew Rybchenko * Add or update a single TLV item in the first segment of a TLV formatted
17225e111ed8SAndrew Rybchenko * dynamic config partition. The first segment is the current active
17235e111ed8SAndrew Rybchenko * configuration.
17245e111ed8SAndrew Rybchenko */
17255e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size)17265e111ed8SAndrew Rybchenko ef10_nvram_partn_write_tlv(
17275e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
17285e111ed8SAndrew Rybchenko __in uint32_t partn,
17295e111ed8SAndrew Rybchenko __in uint32_t tag,
17305e111ed8SAndrew Rybchenko __in_bcount(size) caddr_t data,
17315e111ed8SAndrew Rybchenko __in size_t size)
17325e111ed8SAndrew Rybchenko {
17335e111ed8SAndrew Rybchenko return ef10_nvram_partn_write_segment_tlv(enp, partn, tag, data,
17345e111ed8SAndrew Rybchenko size, B_FALSE);
17355e111ed8SAndrew Rybchenko }
17365e111ed8SAndrew Rybchenko
17375e111ed8SAndrew Rybchenko /*
17385e111ed8SAndrew Rybchenko * Read a segment from nvram at the given offset into a buffer (segment_data)
17395e111ed8SAndrew Rybchenko * and optionally write a new tag to it.
17405e111ed8SAndrew Rybchenko */
17415e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
ef10_nvram_segment_write_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__inout caddr_t * seg_datap,__inout size_t * partn_offsetp,__inout size_t * src_remain_lenp,__inout size_t * dest_remain_lenp,__in boolean_t write)17425e111ed8SAndrew Rybchenko ef10_nvram_segment_write_tlv(
17435e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
17445e111ed8SAndrew Rybchenko __in uint32_t partn,
17455e111ed8SAndrew Rybchenko __in uint32_t tag,
17465e111ed8SAndrew Rybchenko __in_bcount(size) caddr_t data,
17475e111ed8SAndrew Rybchenko __in size_t size,
17485e111ed8SAndrew Rybchenko __inout caddr_t *seg_datap,
17495e111ed8SAndrew Rybchenko __inout size_t *partn_offsetp,
17505e111ed8SAndrew Rybchenko __inout size_t *src_remain_lenp,
17515e111ed8SAndrew Rybchenko __inout size_t *dest_remain_lenp,
17525e111ed8SAndrew Rybchenko __in boolean_t write)
17535e111ed8SAndrew Rybchenko {
17545e111ed8SAndrew Rybchenko efx_rc_t rc;
17555e111ed8SAndrew Rybchenko efx_rc_t status;
17565e111ed8SAndrew Rybchenko size_t original_segment_size;
17575e111ed8SAndrew Rybchenko size_t modified_segment_size;
17585e111ed8SAndrew Rybchenko
17595e111ed8SAndrew Rybchenko /*
17605e111ed8SAndrew Rybchenko * Read the segment from NVRAM into the segment_data buffer and validate
17615e111ed8SAndrew Rybchenko * it, returning if it does not validate. This is not a failure unless
17625e111ed8SAndrew Rybchenko * this is the first segment in a partition. In this case the caller
17635e111ed8SAndrew Rybchenko * must propagate the error.
17645e111ed8SAndrew Rybchenko */
17655e111ed8SAndrew Rybchenko status = ef10_nvram_read_tlv_segment(enp, partn, *partn_offsetp,
17665e111ed8SAndrew Rybchenko *seg_datap, *src_remain_lenp);
17675e111ed8SAndrew Rybchenko if (status != 0) {
17685e111ed8SAndrew Rybchenko rc = EINVAL;
17695e111ed8SAndrew Rybchenko goto fail1;
17705e111ed8SAndrew Rybchenko }
17715e111ed8SAndrew Rybchenko
17725e111ed8SAndrew Rybchenko status = ef10_nvram_buf_segment_size(*seg_datap,
17735e111ed8SAndrew Rybchenko *src_remain_lenp, &original_segment_size);
17745e111ed8SAndrew Rybchenko if (status != 0) {
17755e111ed8SAndrew Rybchenko rc = EINVAL;
17765e111ed8SAndrew Rybchenko goto fail2;
17775e111ed8SAndrew Rybchenko }
17785e111ed8SAndrew Rybchenko
17795e111ed8SAndrew Rybchenko if (write) {
17805e111ed8SAndrew Rybchenko /* Update the contents of the segment in the buffer */
17815e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_buf_write_tlv(*seg_datap,
17825e111ed8SAndrew Rybchenko *dest_remain_lenp, tag, data, size,
17835e111ed8SAndrew Rybchenko &modified_segment_size)) != 0) {
17845e111ed8SAndrew Rybchenko goto fail3;
17855e111ed8SAndrew Rybchenko }
17865e111ed8SAndrew Rybchenko *dest_remain_lenp -= modified_segment_size;
17875e111ed8SAndrew Rybchenko *seg_datap += modified_segment_size;
17885e111ed8SAndrew Rybchenko } else {
17895e111ed8SAndrew Rybchenko /*
17905e111ed8SAndrew Rybchenko * We won't modify this segment, but still need to update the
17915e111ed8SAndrew Rybchenko * remaining lengths and pointers.
17925e111ed8SAndrew Rybchenko */
17935e111ed8SAndrew Rybchenko *dest_remain_lenp -= original_segment_size;
17945e111ed8SAndrew Rybchenko *seg_datap += original_segment_size;
17955e111ed8SAndrew Rybchenko }
17965e111ed8SAndrew Rybchenko
17975e111ed8SAndrew Rybchenko *partn_offsetp += original_segment_size;
17985e111ed8SAndrew Rybchenko *src_remain_lenp -= original_segment_size;
17995e111ed8SAndrew Rybchenko
18005e111ed8SAndrew Rybchenko return (0);
18015e111ed8SAndrew Rybchenko
18025e111ed8SAndrew Rybchenko fail3:
18035e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
18045e111ed8SAndrew Rybchenko fail2:
18055e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
18065e111ed8SAndrew Rybchenko fail1:
18075e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
18085e111ed8SAndrew Rybchenko
18095e111ed8SAndrew Rybchenko return (rc);
18105e111ed8SAndrew Rybchenko }
18115e111ed8SAndrew Rybchenko
18125e111ed8SAndrew Rybchenko /*
18135e111ed8SAndrew Rybchenko * Add or update a single TLV item in either the first segment or in all
18145e111ed8SAndrew Rybchenko * segments in a TLV formatted dynamic config partition. Dynamic config
18155e111ed8SAndrew Rybchenko * partitions on boards that support RFID are divided into a number of segments,
18165e111ed8SAndrew Rybchenko * each formatted like a partition, with header, trailer and end tags. The first
18175e111ed8SAndrew Rybchenko * segment is the current active configuration.
18185e111ed8SAndrew Rybchenko *
18195e111ed8SAndrew Rybchenko * The segments are initialised by manftest and each contain a different
18205e111ed8SAndrew Rybchenko * configuration e.g. firmware variant. The firmware can be instructed
18215e111ed8SAndrew Rybchenko * via RFID to copy a segment to replace the first segment, hence changing the
18225e111ed8SAndrew Rybchenko * active configuration. This allows ops to change the configuration of a board
18235e111ed8SAndrew Rybchenko * prior to shipment using RFID.
18245e111ed8SAndrew Rybchenko *
18255e111ed8SAndrew Rybchenko * Changes to the dynamic config may need to be written to all segments (e.g.
18265e111ed8SAndrew Rybchenko * firmware versions) or just the first segment (changes to the active
18275e111ed8SAndrew Rybchenko * configuration). See SF-111324-SW "The use of RFID in Solarflare Products".
18285e111ed8SAndrew Rybchenko * If only the first segment is written the code still needs to be aware of the
18295e111ed8SAndrew Rybchenko * possible presence of subsequent segments as writing to a segment may cause
18305e111ed8SAndrew Rybchenko * its size to increase, which would overwrite the subsequent segments and
18315e111ed8SAndrew Rybchenko * invalidate them.
18325e111ed8SAndrew Rybchenko */
18335e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_write_segment_tlv(__in efx_nic_t * enp,__in uint32_t partn,__in uint32_t tag,__in_bcount (size)caddr_t data,__in size_t size,__in boolean_t all_segments)18345e111ed8SAndrew Rybchenko ef10_nvram_partn_write_segment_tlv(
18355e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
18365e111ed8SAndrew Rybchenko __in uint32_t partn,
18375e111ed8SAndrew Rybchenko __in uint32_t tag,
18385e111ed8SAndrew Rybchenko __in_bcount(size) caddr_t data,
18395e111ed8SAndrew Rybchenko __in size_t size,
18405e111ed8SAndrew Rybchenko __in boolean_t all_segments)
18415e111ed8SAndrew Rybchenko {
18425e111ed8SAndrew Rybchenko size_t partn_size = 0;
18435e111ed8SAndrew Rybchenko caddr_t partn_data;
18445e111ed8SAndrew Rybchenko size_t total_length = 0;
18455e111ed8SAndrew Rybchenko efx_rc_t rc;
18465e111ed8SAndrew Rybchenko size_t current_offset = 0;
18475e111ed8SAndrew Rybchenko size_t remaining_original_length;
18485e111ed8SAndrew Rybchenko size_t remaining_modified_length;
18495e111ed8SAndrew Rybchenko caddr_t segment_data;
18505e111ed8SAndrew Rybchenko
18515e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(partn, ==, NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG);
18525e111ed8SAndrew Rybchenko
18535e111ed8SAndrew Rybchenko /* Allocate sufficient memory for the entire partition */
18545e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_size(enp, partn, &partn_size)) != 0)
18555e111ed8SAndrew Rybchenko goto fail1;
18565e111ed8SAndrew Rybchenko
18575e111ed8SAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, partn_size, partn_data);
18585e111ed8SAndrew Rybchenko if (partn_data == NULL) {
18595e111ed8SAndrew Rybchenko rc = ENOMEM;
18605e111ed8SAndrew Rybchenko goto fail2;
18615e111ed8SAndrew Rybchenko }
18625e111ed8SAndrew Rybchenko
18635e111ed8SAndrew Rybchenko remaining_original_length = partn_size;
18645e111ed8SAndrew Rybchenko remaining_modified_length = partn_size;
18655e111ed8SAndrew Rybchenko segment_data = partn_data;
18665e111ed8SAndrew Rybchenko
18675e111ed8SAndrew Rybchenko /* Lock the partition */
18685e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
18695e111ed8SAndrew Rybchenko goto fail3;
18705e111ed8SAndrew Rybchenko
18715e111ed8SAndrew Rybchenko /* Iterate over each (potential) segment to update it. */
18725e111ed8SAndrew Rybchenko do {
18735e111ed8SAndrew Rybchenko boolean_t write = all_segments || current_offset == 0;
18745e111ed8SAndrew Rybchenko
18755e111ed8SAndrew Rybchenko rc = ef10_nvram_segment_write_tlv(enp, partn, tag, data, size,
18765e111ed8SAndrew Rybchenko &segment_data, ¤t_offset, &remaining_original_length,
18775e111ed8SAndrew Rybchenko &remaining_modified_length, write);
18785e111ed8SAndrew Rybchenko if (rc != 0) {
18795e111ed8SAndrew Rybchenko if (current_offset == 0) {
18805e111ed8SAndrew Rybchenko /*
18815e111ed8SAndrew Rybchenko * If no data has been read then the first
18825e111ed8SAndrew Rybchenko * segment is invalid, which is an error.
18835e111ed8SAndrew Rybchenko */
18845e111ed8SAndrew Rybchenko goto fail4;
18855e111ed8SAndrew Rybchenko }
18865e111ed8SAndrew Rybchenko break;
18875e111ed8SAndrew Rybchenko }
18885e111ed8SAndrew Rybchenko } while (current_offset < partn_size);
18895e111ed8SAndrew Rybchenko
18905e111ed8SAndrew Rybchenko total_length = segment_data - partn_data;
18915e111ed8SAndrew Rybchenko
18925e111ed8SAndrew Rybchenko /*
18935e111ed8SAndrew Rybchenko * We've run out of space. This should actually be dealt with by
18945e111ed8SAndrew Rybchenko * ef10_nvram_buf_write_tlv returning ENOSPC.
18955e111ed8SAndrew Rybchenko */
18965e111ed8SAndrew Rybchenko if (total_length > partn_size) {
18975e111ed8SAndrew Rybchenko rc = ENOSPC;
18985e111ed8SAndrew Rybchenko goto fail5;
18995e111ed8SAndrew Rybchenko }
19005e111ed8SAndrew Rybchenko
19015e111ed8SAndrew Rybchenko /* Erase the whole partition in NVRAM */
19025e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_erase(enp, partn, 0, partn_size)) != 0)
19035e111ed8SAndrew Rybchenko goto fail6;
19045e111ed8SAndrew Rybchenko
19055e111ed8SAndrew Rybchenko /* Write new partition contents from the buffer to NVRAM */
19065e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_write(enp, partn, 0, partn_data,
19075e111ed8SAndrew Rybchenko total_length)) != 0)
19085e111ed8SAndrew Rybchenko goto fail7;
19095e111ed8SAndrew Rybchenko
19105e111ed8SAndrew Rybchenko /* Unlock the partition */
19115e111ed8SAndrew Rybchenko (void) ef10_nvram_partn_unlock(enp, partn, NULL);
19125e111ed8SAndrew Rybchenko
19135e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
19145e111ed8SAndrew Rybchenko
19155e111ed8SAndrew Rybchenko return (0);
19165e111ed8SAndrew Rybchenko
19175e111ed8SAndrew Rybchenko fail7:
19185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail7);
19195e111ed8SAndrew Rybchenko fail6:
19205e111ed8SAndrew Rybchenko EFSYS_PROBE(fail6);
19215e111ed8SAndrew Rybchenko fail5:
19225e111ed8SAndrew Rybchenko EFSYS_PROBE(fail5);
19235e111ed8SAndrew Rybchenko fail4:
19245e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
19255e111ed8SAndrew Rybchenko
19265e111ed8SAndrew Rybchenko (void) ef10_nvram_partn_unlock(enp, partn, NULL);
19275e111ed8SAndrew Rybchenko fail3:
19285e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
19295e111ed8SAndrew Rybchenko
19305e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, partn_size, partn_data);
19315e111ed8SAndrew Rybchenko fail2:
19325e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
19335e111ed8SAndrew Rybchenko fail1:
19345e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
19355e111ed8SAndrew Rybchenko
19365e111ed8SAndrew Rybchenko return (rc);
19375e111ed8SAndrew Rybchenko }
19385e111ed8SAndrew Rybchenko
19395e111ed8SAndrew Rybchenko /*
19405e111ed8SAndrew Rybchenko * Get the size of a NVRAM partition. This is the total size allocated in nvram,
19415e111ed8SAndrew Rybchenko * not the data used by the segments in the partition.
19425e111ed8SAndrew Rybchenko */
19435e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_size(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * sizep)19445e111ed8SAndrew Rybchenko ef10_nvram_partn_size(
19455e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
19465e111ed8SAndrew Rybchenko __in uint32_t partn,
19475e111ed8SAndrew Rybchenko __out size_t *sizep)
19485e111ed8SAndrew Rybchenko {
19495e111ed8SAndrew Rybchenko efx_rc_t rc;
19505e111ed8SAndrew Rybchenko efx_nvram_info_t eni = { 0 };
19515e111ed8SAndrew Rybchenko
19525e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
19535e111ed8SAndrew Rybchenko goto fail1;
19545e111ed8SAndrew Rybchenko
19555e111ed8SAndrew Rybchenko *sizep = eni.eni_partn_size;
19565e111ed8SAndrew Rybchenko
19575e111ed8SAndrew Rybchenko return (0);
19585e111ed8SAndrew Rybchenko
19595e111ed8SAndrew Rybchenko fail1:
19605e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
19615e111ed8SAndrew Rybchenko
19625e111ed8SAndrew Rybchenko return (rc);
19635e111ed8SAndrew Rybchenko }
19645e111ed8SAndrew Rybchenko
19655e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_info(__in efx_nic_t * enp,__in uint32_t partn,__out efx_nvram_info_t * enip)19665e111ed8SAndrew Rybchenko ef10_nvram_partn_info(
19675e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
19685e111ed8SAndrew Rybchenko __in uint32_t partn,
19695e111ed8SAndrew Rybchenko __out efx_nvram_info_t *enip)
19705e111ed8SAndrew Rybchenko {
19715e111ed8SAndrew Rybchenko efx_rc_t rc;
19725e111ed8SAndrew Rybchenko
19735e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_info(enp, partn, enip)) != 0)
19745e111ed8SAndrew Rybchenko goto fail1;
19755e111ed8SAndrew Rybchenko
19765e111ed8SAndrew Rybchenko if (enip->eni_write_size == 0)
19775e111ed8SAndrew Rybchenko enip->eni_write_size = EF10_NVRAM_CHUNK;
19785e111ed8SAndrew Rybchenko
19795e111ed8SAndrew Rybchenko return (0);
19805e111ed8SAndrew Rybchenko
19815e111ed8SAndrew Rybchenko fail1:
19825e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
19835e111ed8SAndrew Rybchenko
19845e111ed8SAndrew Rybchenko return (rc);
19855e111ed8SAndrew Rybchenko }
19865e111ed8SAndrew Rybchenko
19875e111ed8SAndrew Rybchenko
19885e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_lock(__in efx_nic_t * enp,__in uint32_t partn)19895e111ed8SAndrew Rybchenko ef10_nvram_partn_lock(
19905e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
19915e111ed8SAndrew Rybchenko __in uint32_t partn)
19925e111ed8SAndrew Rybchenko {
19935e111ed8SAndrew Rybchenko efx_rc_t rc;
19945e111ed8SAndrew Rybchenko
19955e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0)
19965e111ed8SAndrew Rybchenko goto fail1;
19975e111ed8SAndrew Rybchenko
19985e111ed8SAndrew Rybchenko return (0);
19995e111ed8SAndrew Rybchenko
20005e111ed8SAndrew Rybchenko fail1:
20015e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
20025e111ed8SAndrew Rybchenko
20035e111ed8SAndrew Rybchenko return (rc);
20045e111ed8SAndrew Rybchenko }
20055e111ed8SAndrew Rybchenko
20065e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_read_mode(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size,__in uint32_t mode)20075e111ed8SAndrew Rybchenko ef10_nvram_partn_read_mode(
20085e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
20095e111ed8SAndrew Rybchenko __in uint32_t partn,
20105e111ed8SAndrew Rybchenko __in unsigned int offset,
20115e111ed8SAndrew Rybchenko __out_bcount(size) caddr_t data,
20125e111ed8SAndrew Rybchenko __in size_t size,
20135e111ed8SAndrew Rybchenko __in uint32_t mode)
20145e111ed8SAndrew Rybchenko {
20155e111ed8SAndrew Rybchenko size_t chunk;
20165e111ed8SAndrew Rybchenko efx_rc_t rc;
20175e111ed8SAndrew Rybchenko
20185e111ed8SAndrew Rybchenko while (size > 0) {
20195e111ed8SAndrew Rybchenko chunk = MIN(size, EF10_NVRAM_CHUNK);
20205e111ed8SAndrew Rybchenko
20215e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
20225e111ed8SAndrew Rybchenko data, chunk, mode)) != 0) {
20235e111ed8SAndrew Rybchenko goto fail1;
20245e111ed8SAndrew Rybchenko }
20255e111ed8SAndrew Rybchenko
20265e111ed8SAndrew Rybchenko size -= chunk;
20275e111ed8SAndrew Rybchenko data += chunk;
20285e111ed8SAndrew Rybchenko offset += chunk;
20295e111ed8SAndrew Rybchenko }
20305e111ed8SAndrew Rybchenko
20315e111ed8SAndrew Rybchenko return (0);
20325e111ed8SAndrew Rybchenko
20335e111ed8SAndrew Rybchenko fail1:
20345e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
20355e111ed8SAndrew Rybchenko
20365e111ed8SAndrew Rybchenko return (rc);
20375e111ed8SAndrew Rybchenko }
20385e111ed8SAndrew Rybchenko
20395e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_read(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)20405e111ed8SAndrew Rybchenko ef10_nvram_partn_read(
20415e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
20425e111ed8SAndrew Rybchenko __in uint32_t partn,
20435e111ed8SAndrew Rybchenko __in unsigned int offset,
20445e111ed8SAndrew Rybchenko __out_bcount(size) caddr_t data,
20455e111ed8SAndrew Rybchenko __in size_t size)
20465e111ed8SAndrew Rybchenko {
20475e111ed8SAndrew Rybchenko /*
20485e111ed8SAndrew Rybchenko * An A/B partition has two data stores (current and backup).
20495e111ed8SAndrew Rybchenko * Read requests which come in through the EFX API expect to read the
20505e111ed8SAndrew Rybchenko * current, active store of an A/B partition. For non A/B partitions,
20515e111ed8SAndrew Rybchenko * there is only a single store and so the mode param is ignored.
20525e111ed8SAndrew Rybchenko */
20535e111ed8SAndrew Rybchenko return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
20545e111ed8SAndrew Rybchenko MC_CMD_NVRAM_READ_IN_V2_TARGET_CURRENT);
20555e111ed8SAndrew Rybchenko }
20565e111ed8SAndrew Rybchenko
20575e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_read_backup(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__out_bcount (size)caddr_t data,__in size_t size)20585e111ed8SAndrew Rybchenko ef10_nvram_partn_read_backup(
20595e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
20605e111ed8SAndrew Rybchenko __in uint32_t partn,
20615e111ed8SAndrew Rybchenko __in unsigned int offset,
20625e111ed8SAndrew Rybchenko __out_bcount(size) caddr_t data,
20635e111ed8SAndrew Rybchenko __in size_t size)
20645e111ed8SAndrew Rybchenko {
20655e111ed8SAndrew Rybchenko /*
20665e111ed8SAndrew Rybchenko * An A/B partition has two data stores (current and backup).
20675e111ed8SAndrew Rybchenko * Read the backup store of an A/B partition (i.e. the store currently
20685e111ed8SAndrew Rybchenko * being written to if the partition is locked).
20695e111ed8SAndrew Rybchenko *
20705e111ed8SAndrew Rybchenko * This is needed when comparing the existing partition content to avoid
20715e111ed8SAndrew Rybchenko * unnecessary writes, or to read back what has been written to check
20725e111ed8SAndrew Rybchenko * that the writes have succeeded.
20735e111ed8SAndrew Rybchenko */
20745e111ed8SAndrew Rybchenko return ef10_nvram_partn_read_mode(enp, partn, offset, data, size,
20755e111ed8SAndrew Rybchenko MC_CMD_NVRAM_READ_IN_V2_TARGET_BACKUP);
20765e111ed8SAndrew Rybchenko }
20775e111ed8SAndrew Rybchenko
20785e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_erase(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__in size_t size)20795e111ed8SAndrew Rybchenko ef10_nvram_partn_erase(
20805e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
20815e111ed8SAndrew Rybchenko __in uint32_t partn,
20825e111ed8SAndrew Rybchenko __in unsigned int offset,
20835e111ed8SAndrew Rybchenko __in size_t size)
20845e111ed8SAndrew Rybchenko {
20855e111ed8SAndrew Rybchenko efx_rc_t rc;
20865e111ed8SAndrew Rybchenko efx_nvram_info_t eni = { 0 };
20875e111ed8SAndrew Rybchenko uint32_t erase_size;
20885e111ed8SAndrew Rybchenko
20895e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
20905e111ed8SAndrew Rybchenko goto fail1;
20915e111ed8SAndrew Rybchenko
20925e111ed8SAndrew Rybchenko erase_size = eni.eni_erase_size;
20935e111ed8SAndrew Rybchenko
20945e111ed8SAndrew Rybchenko if (erase_size == 0) {
20955e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0)
20965e111ed8SAndrew Rybchenko goto fail2;
20975e111ed8SAndrew Rybchenko } else {
20985e111ed8SAndrew Rybchenko if (size % erase_size != 0) {
20995e111ed8SAndrew Rybchenko rc = EINVAL;
21005e111ed8SAndrew Rybchenko goto fail3;
21015e111ed8SAndrew Rybchenko }
21025e111ed8SAndrew Rybchenko while (size > 0) {
21035e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_erase(enp, partn, offset,
21045e111ed8SAndrew Rybchenko erase_size)) != 0)
21055e111ed8SAndrew Rybchenko goto fail4;
21065e111ed8SAndrew Rybchenko offset += erase_size;
21075e111ed8SAndrew Rybchenko size -= erase_size;
21085e111ed8SAndrew Rybchenko }
21095e111ed8SAndrew Rybchenko }
21105e111ed8SAndrew Rybchenko
21115e111ed8SAndrew Rybchenko return (0);
21125e111ed8SAndrew Rybchenko
21135e111ed8SAndrew Rybchenko fail4:
21145e111ed8SAndrew Rybchenko EFSYS_PROBE(fail4);
21155e111ed8SAndrew Rybchenko fail3:
21165e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
21175e111ed8SAndrew Rybchenko fail2:
21185e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
21195e111ed8SAndrew Rybchenko fail1:
21205e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
21215e111ed8SAndrew Rybchenko
21225e111ed8SAndrew Rybchenko return (rc);
21235e111ed8SAndrew Rybchenko }
21245e111ed8SAndrew Rybchenko
21255e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_write(__in efx_nic_t * enp,__in uint32_t partn,__in unsigned int offset,__in_bcount (size)caddr_t data,__in size_t size)21265e111ed8SAndrew Rybchenko ef10_nvram_partn_write(
21275e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
21285e111ed8SAndrew Rybchenko __in uint32_t partn,
21295e111ed8SAndrew Rybchenko __in unsigned int offset,
21305e111ed8SAndrew Rybchenko __in_bcount(size) caddr_t data,
21315e111ed8SAndrew Rybchenko __in size_t size)
21325e111ed8SAndrew Rybchenko {
21335e111ed8SAndrew Rybchenko size_t chunk;
21345e111ed8SAndrew Rybchenko efx_nvram_info_t eni = { 0 };
21355e111ed8SAndrew Rybchenko uint32_t write_size;
21365e111ed8SAndrew Rybchenko efx_rc_t rc;
21375e111ed8SAndrew Rybchenko
21385e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_info(enp, partn, &eni)) != 0)
21395e111ed8SAndrew Rybchenko goto fail1;
21405e111ed8SAndrew Rybchenko
21415e111ed8SAndrew Rybchenko write_size = eni.eni_write_size;
21425e111ed8SAndrew Rybchenko
21435e111ed8SAndrew Rybchenko if (write_size != 0) {
21445e111ed8SAndrew Rybchenko /*
21455e111ed8SAndrew Rybchenko * Check that the size is a multiple of the write chunk size if
21465e111ed8SAndrew Rybchenko * the write chunk size is available.
21475e111ed8SAndrew Rybchenko */
21485e111ed8SAndrew Rybchenko if (size % write_size != 0) {
21495e111ed8SAndrew Rybchenko rc = EINVAL;
21505e111ed8SAndrew Rybchenko goto fail2;
21515e111ed8SAndrew Rybchenko }
21525e111ed8SAndrew Rybchenko } else {
21535e111ed8SAndrew Rybchenko write_size = EF10_NVRAM_CHUNK;
21545e111ed8SAndrew Rybchenko }
21555e111ed8SAndrew Rybchenko
21565e111ed8SAndrew Rybchenko while (size > 0) {
21575e111ed8SAndrew Rybchenko chunk = MIN(size, write_size);
21585e111ed8SAndrew Rybchenko
21595e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_write(enp, partn, offset,
21605e111ed8SAndrew Rybchenko data, chunk)) != 0) {
21615e111ed8SAndrew Rybchenko goto fail3;
21625e111ed8SAndrew Rybchenko }
21635e111ed8SAndrew Rybchenko
21645e111ed8SAndrew Rybchenko size -= chunk;
21655e111ed8SAndrew Rybchenko data += chunk;
21665e111ed8SAndrew Rybchenko offset += chunk;
21675e111ed8SAndrew Rybchenko }
21685e111ed8SAndrew Rybchenko
21695e111ed8SAndrew Rybchenko return (0);
21705e111ed8SAndrew Rybchenko
21715e111ed8SAndrew Rybchenko fail3:
21725e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
21735e111ed8SAndrew Rybchenko fail2:
21745e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
21755e111ed8SAndrew Rybchenko fail1:
21765e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
21775e111ed8SAndrew Rybchenko
21785e111ed8SAndrew Rybchenko return (rc);
21795e111ed8SAndrew Rybchenko }
21805e111ed8SAndrew Rybchenko
21815e111ed8SAndrew Rybchenko #define EF10_NVRAM_INITIAL_POLL_DELAY_US 10000
21825e111ed8SAndrew Rybchenko #define EF10_NVRAM_MAX_POLL_DELAY_US 1000000
21835e111ed8SAndrew Rybchenko #define EF10_NVRAM_POLL_RETRIES 100
21845e111ed8SAndrew Rybchenko
21855e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_unlock(__in efx_nic_t * enp,__in uint32_t partn,__out_opt uint32_t * verify_resultp)21865e111ed8SAndrew Rybchenko ef10_nvram_partn_unlock(
21875e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
21885e111ed8SAndrew Rybchenko __in uint32_t partn,
21895e111ed8SAndrew Rybchenko __out_opt uint32_t *verify_resultp)
21905e111ed8SAndrew Rybchenko {
21915e111ed8SAndrew Rybchenko boolean_t reboot = B_FALSE;
21925e111ed8SAndrew Rybchenko uint32_t poll_delay_us = EF10_NVRAM_INITIAL_POLL_DELAY_US;
21935e111ed8SAndrew Rybchenko uint32_t poll_retry = 0;
21945e111ed8SAndrew Rybchenko uint32_t verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
21955e111ed8SAndrew Rybchenko efx_rc_t rc;
21965e111ed8SAndrew Rybchenko
21975e111ed8SAndrew Rybchenko rc = efx_mcdi_nvram_update_finish(enp, partn, reboot,
21985e111ed8SAndrew Rybchenko EFX_NVRAM_UPDATE_FLAGS_BACKGROUND, &verify_result);
21995e111ed8SAndrew Rybchenko
22005e111ed8SAndrew Rybchenko /*
22015e111ed8SAndrew Rybchenko * NVRAM updates can take a long time (e.g. up to 1 minute for bundle
22025e111ed8SAndrew Rybchenko * images). Polling for NVRAM update completion ensures that other MCDI
22035e111ed8SAndrew Rybchenko * commands can be issued before the background NVRAM update completes.
22045e111ed8SAndrew Rybchenko *
22055e111ed8SAndrew Rybchenko * Without polling, other MCDI commands can only be issued before the
22065e111ed8SAndrew Rybchenko * NVRAM update completes if the MCDI transport and the firmware
22075e111ed8SAndrew Rybchenko * support the Asynchronous MCDI protocol extensions in SF-116575-PS.
22085e111ed8SAndrew Rybchenko *
22095e111ed8SAndrew Rybchenko * The initial call either completes the update synchronously, or
22105e111ed8SAndrew Rybchenko * returns RC_PENDING to indicate processing is continuing. In the
22115e111ed8SAndrew Rybchenko * latter case, we poll for at least 1 minute, at increasing intervals
22125e111ed8SAndrew Rybchenko * (10ms, 100ms, 1s).
22135e111ed8SAndrew Rybchenko */
22145e111ed8SAndrew Rybchenko while (verify_result == MC_CMD_NVRAM_VERIFY_RC_PENDING) {
22155e111ed8SAndrew Rybchenko
22165e111ed8SAndrew Rybchenko if (poll_retry > EF10_NVRAM_POLL_RETRIES) {
22175e111ed8SAndrew Rybchenko rc = ETIMEDOUT;
22185e111ed8SAndrew Rybchenko goto fail1;
22195e111ed8SAndrew Rybchenko }
22205e111ed8SAndrew Rybchenko poll_retry++;
22215e111ed8SAndrew Rybchenko
22225e111ed8SAndrew Rybchenko EFSYS_SLEEP(poll_delay_us);
22235e111ed8SAndrew Rybchenko if (poll_delay_us < EF10_NVRAM_MAX_POLL_DELAY_US)
22245e111ed8SAndrew Rybchenko poll_delay_us *= 10;
22255e111ed8SAndrew Rybchenko
22265e111ed8SAndrew Rybchenko /* Poll for completion of background NVRAM update. */
22275e111ed8SAndrew Rybchenko verify_result = MC_CMD_NVRAM_VERIFY_RC_UNKNOWN;
22285e111ed8SAndrew Rybchenko
22295e111ed8SAndrew Rybchenko rc = efx_mcdi_nvram_update_finish(enp, partn, reboot,
22305e111ed8SAndrew Rybchenko EFX_NVRAM_UPDATE_FLAGS_POLL, &verify_result);
22315e111ed8SAndrew Rybchenko if (rc != 0) {
22325e111ed8SAndrew Rybchenko /* Poll failed, so assume NVRAM update failed. */
22335e111ed8SAndrew Rybchenko goto fail2;
22345e111ed8SAndrew Rybchenko }
22355e111ed8SAndrew Rybchenko }
22365e111ed8SAndrew Rybchenko
22375e111ed8SAndrew Rybchenko if (verify_resultp != NULL)
22385e111ed8SAndrew Rybchenko *verify_resultp = verify_result;
22395e111ed8SAndrew Rybchenko
22405e111ed8SAndrew Rybchenko return (0);
22415e111ed8SAndrew Rybchenko
22425e111ed8SAndrew Rybchenko fail2:
22435e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
22445e111ed8SAndrew Rybchenko fail1:
22455e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
22465e111ed8SAndrew Rybchenko
22475e111ed8SAndrew Rybchenko return (rc);
22485e111ed8SAndrew Rybchenko }
22495e111ed8SAndrew Rybchenko
22505e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
22515e111ed8SAndrew Rybchenko ef10_nvram_partn_set_version(
22525e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
22535e111ed8SAndrew Rybchenko __in uint32_t partn,
22545e111ed8SAndrew Rybchenko __in_ecount(4) uint16_t version[4])
22555e111ed8SAndrew Rybchenko {
22565e111ed8SAndrew Rybchenko struct tlv_partition_version partn_version;
22575e111ed8SAndrew Rybchenko size_t size;
22585e111ed8SAndrew Rybchenko efx_rc_t rc;
22595e111ed8SAndrew Rybchenko
22605e111ed8SAndrew Rybchenko /* Add or modify partition version TLV item */
22615e111ed8SAndrew Rybchenko partn_version.version_w = __CPU_TO_LE_16(version[0]);
22625e111ed8SAndrew Rybchenko partn_version.version_x = __CPU_TO_LE_16(version[1]);
22635e111ed8SAndrew Rybchenko partn_version.version_y = __CPU_TO_LE_16(version[2]);
22645e111ed8SAndrew Rybchenko partn_version.version_z = __CPU_TO_LE_16(version[3]);
22655e111ed8SAndrew Rybchenko
22665e111ed8SAndrew Rybchenko size = sizeof (partn_version) - (2 * sizeof (uint32_t));
22675e111ed8SAndrew Rybchenko
22685e111ed8SAndrew Rybchenko /* Write the version number to all segments in the partition */
22695e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_write_segment_tlv(enp,
22705e111ed8SAndrew Rybchenko NVRAM_PARTITION_TYPE_DYNAMIC_CONFIG,
22715e111ed8SAndrew Rybchenko TLV_TAG_PARTITION_VERSION(partn),
22725e111ed8SAndrew Rybchenko (caddr_t)&partn_version.version_w, size, B_TRUE)) != 0)
22735e111ed8SAndrew Rybchenko goto fail1;
22745e111ed8SAndrew Rybchenko
22755e111ed8SAndrew Rybchenko return (0);
22765e111ed8SAndrew Rybchenko
22775e111ed8SAndrew Rybchenko fail1:
22785e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
22795e111ed8SAndrew Rybchenko
22805e111ed8SAndrew Rybchenko return (rc);
22815e111ed8SAndrew Rybchenko }
22825e111ed8SAndrew Rybchenko
22835e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
22845e111ed8SAndrew Rybchenko
22855e111ed8SAndrew Rybchenko #if EFSYS_OPT_NVRAM
22865e111ed8SAndrew Rybchenko
22875e111ed8SAndrew Rybchenko typedef struct ef10_parttbl_entry_s {
22885e111ed8SAndrew Rybchenko unsigned int partn;
22895e111ed8SAndrew Rybchenko unsigned int port_mask;
22905e111ed8SAndrew Rybchenko efx_nvram_type_t nvtype;
22915e111ed8SAndrew Rybchenko } ef10_parttbl_entry_t;
22925e111ed8SAndrew Rybchenko
22935e111ed8SAndrew Rybchenko /* Port mask values */
22945e111ed8SAndrew Rybchenko #define PORT_1 (1u << 1)
22955e111ed8SAndrew Rybchenko #define PORT_2 (1u << 2)
22965e111ed8SAndrew Rybchenko #define PORT_3 (1u << 3)
22975e111ed8SAndrew Rybchenko #define PORT_4 (1u << 4)
22985e111ed8SAndrew Rybchenko #define PORT_ALL (0xffffffffu)
22995e111ed8SAndrew Rybchenko
23005e111ed8SAndrew Rybchenko #define PARTN_MAP_ENTRY(partn, port_mask, nvtype) \
23015e111ed8SAndrew Rybchenko { (NVRAM_PARTITION_TYPE_##partn), (PORT_##port_mask), (EFX_NVRAM_##nvtype) }
23025e111ed8SAndrew Rybchenko
23035e111ed8SAndrew Rybchenko /* Translate EFX NVRAM types to firmware partition types */
23045e111ed8SAndrew Rybchenko static ef10_parttbl_entry_t hunt_parttbl[] = {
23055e111ed8SAndrew Rybchenko /* partn ports nvtype */
23065e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
23075e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
23085e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
23095e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT0, 1, BOOTROM_CFG),
23105e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT1, 2, BOOTROM_CFG),
23115e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT2, 3, BOOTROM_CFG),
23125e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG_PORT3, 4, BOOTROM_CFG),
23135e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
23145e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
23155e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
23165e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
23175e111ed8SAndrew Rybchenko };
23185e111ed8SAndrew Rybchenko
23195e111ed8SAndrew Rybchenko static ef10_parttbl_entry_t medford_parttbl[] = {
23205e111ed8SAndrew Rybchenko /* partn ports nvtype */
23215e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
23225e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
23235e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
23245e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
23255e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
23265e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
23275e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
23285e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
23295e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
23305e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
23315e111ed8SAndrew Rybchenko };
23325e111ed8SAndrew Rybchenko
23335e111ed8SAndrew Rybchenko static ef10_parttbl_entry_t medford2_parttbl[] = {
23345e111ed8SAndrew Rybchenko /* partn ports nvtype */
23355e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE, ALL, MC_FIRMWARE),
23365e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MC_FIRMWARE_BACKUP, ALL, MC_GOLDEN),
23375e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPANSION_ROM, ALL, BOOTROM),
23385e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPROM_CONFIG, ALL, BOOTROM_CFG),
23395e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(DYNAMIC_CONFIG, ALL, DYNAMIC_CFG),
23405e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA, ALL, FPGA),
23415e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(FPGA_BACKUP, ALL, FPGA_BACKUP),
23425e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(LICENSE, ALL, LICENSE),
23435e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(EXPANSION_UEFI, ALL, UEFIROM),
23445e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(MUM_FIRMWARE, ALL, MUM_FIRMWARE),
23455e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(DYNCONFIG_DEFAULTS, ALL, DYNCONFIG_DEFAULTS),
23465e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(ROMCONFIG_DEFAULTS, ALL, ROMCONFIG_DEFAULTS),
23475e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(BUNDLE, ALL, BUNDLE),
23485e111ed8SAndrew Rybchenko PARTN_MAP_ENTRY(BUNDLE_METADATA, ALL, BUNDLE_METADATA),
23495e111ed8SAndrew Rybchenko };
23505e111ed8SAndrew Rybchenko
23515e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
ef10_parttbl_get(__in efx_nic_t * enp,__out ef10_parttbl_entry_t ** parttblp,__out size_t * parttbl_rowsp)23525e111ed8SAndrew Rybchenko ef10_parttbl_get(
23535e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
23545e111ed8SAndrew Rybchenko __out ef10_parttbl_entry_t **parttblp,
23555e111ed8SAndrew Rybchenko __out size_t *parttbl_rowsp)
23565e111ed8SAndrew Rybchenko {
23575e111ed8SAndrew Rybchenko switch (enp->en_family) {
23585e111ed8SAndrew Rybchenko case EFX_FAMILY_HUNTINGTON:
23595e111ed8SAndrew Rybchenko *parttblp = hunt_parttbl;
23605e111ed8SAndrew Rybchenko *parttbl_rowsp = EFX_ARRAY_SIZE(hunt_parttbl);
23615e111ed8SAndrew Rybchenko break;
23625e111ed8SAndrew Rybchenko
23635e111ed8SAndrew Rybchenko case EFX_FAMILY_MEDFORD:
23645e111ed8SAndrew Rybchenko *parttblp = medford_parttbl;
23655e111ed8SAndrew Rybchenko *parttbl_rowsp = EFX_ARRAY_SIZE(medford_parttbl);
23665e111ed8SAndrew Rybchenko break;
23675e111ed8SAndrew Rybchenko
23685e111ed8SAndrew Rybchenko case EFX_FAMILY_MEDFORD2:
23695e111ed8SAndrew Rybchenko *parttblp = medford2_parttbl;
23705e111ed8SAndrew Rybchenko *parttbl_rowsp = EFX_ARRAY_SIZE(medford2_parttbl);
23715e111ed8SAndrew Rybchenko break;
23725e111ed8SAndrew Rybchenko
23735e111ed8SAndrew Rybchenko default:
23745e111ed8SAndrew Rybchenko EFSYS_ASSERT(B_FALSE);
23755e111ed8SAndrew Rybchenko return (EINVAL);
23765e111ed8SAndrew Rybchenko }
23775e111ed8SAndrew Rybchenko return (0);
23785e111ed8SAndrew Rybchenko }
23795e111ed8SAndrew Rybchenko
23805e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_type_to_partn(__in efx_nic_t * enp,__in efx_nvram_type_t type,__out uint32_t * partnp)23815e111ed8SAndrew Rybchenko ef10_nvram_type_to_partn(
23825e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
23835e111ed8SAndrew Rybchenko __in efx_nvram_type_t type,
23845e111ed8SAndrew Rybchenko __out uint32_t *partnp)
23855e111ed8SAndrew Rybchenko {
23865e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
23875e111ed8SAndrew Rybchenko ef10_parttbl_entry_t *parttbl = NULL;
23885e111ed8SAndrew Rybchenko size_t parttbl_rows = 0;
23895e111ed8SAndrew Rybchenko unsigned int i;
23905e111ed8SAndrew Rybchenko
23915e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(type, !=, EFX_NVRAM_INVALID);
23925e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
23935e111ed8SAndrew Rybchenko EFSYS_ASSERT(partnp != NULL);
23945e111ed8SAndrew Rybchenko
23955e111ed8SAndrew Rybchenko if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
23965e111ed8SAndrew Rybchenko for (i = 0; i < parttbl_rows; i++) {
23975e111ed8SAndrew Rybchenko ef10_parttbl_entry_t *entry = &parttbl[i];
23985e111ed8SAndrew Rybchenko
23995e111ed8SAndrew Rybchenko if ((entry->nvtype == type) &&
24005e111ed8SAndrew Rybchenko (entry->port_mask & (1u << emip->emi_port))) {
24015e111ed8SAndrew Rybchenko *partnp = entry->partn;
24025e111ed8SAndrew Rybchenko return (0);
24035e111ed8SAndrew Rybchenko }
24045e111ed8SAndrew Rybchenko }
24055e111ed8SAndrew Rybchenko }
24065e111ed8SAndrew Rybchenko
24075e111ed8SAndrew Rybchenko return (ENOTSUP);
24085e111ed8SAndrew Rybchenko }
24095e111ed8SAndrew Rybchenko
24105e111ed8SAndrew Rybchenko #if EFSYS_OPT_DIAG
24115e111ed8SAndrew Rybchenko
24125e111ed8SAndrew Rybchenko static __checkReturn efx_rc_t
ef10_nvram_partn_to_type(__in efx_nic_t * enp,__in uint32_t partn,__out efx_nvram_type_t * typep)24135e111ed8SAndrew Rybchenko ef10_nvram_partn_to_type(
24145e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
24155e111ed8SAndrew Rybchenko __in uint32_t partn,
24165e111ed8SAndrew Rybchenko __out efx_nvram_type_t *typep)
24175e111ed8SAndrew Rybchenko {
24185e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
24195e111ed8SAndrew Rybchenko ef10_parttbl_entry_t *parttbl = NULL;
24205e111ed8SAndrew Rybchenko size_t parttbl_rows = 0;
24215e111ed8SAndrew Rybchenko unsigned int i;
24225e111ed8SAndrew Rybchenko
24235e111ed8SAndrew Rybchenko EFSYS_ASSERT(typep != NULL);
24245e111ed8SAndrew Rybchenko
24255e111ed8SAndrew Rybchenko if (ef10_parttbl_get(enp, &parttbl, &parttbl_rows) == 0) {
24265e111ed8SAndrew Rybchenko for (i = 0; i < parttbl_rows; i++) {
24275e111ed8SAndrew Rybchenko ef10_parttbl_entry_t *entry = &parttbl[i];
24285e111ed8SAndrew Rybchenko
24295e111ed8SAndrew Rybchenko if ((entry->partn == partn) &&
24305e111ed8SAndrew Rybchenko (entry->port_mask & (1u << emip->emi_port))) {
24315e111ed8SAndrew Rybchenko *typep = entry->nvtype;
24325e111ed8SAndrew Rybchenko return (0);
24335e111ed8SAndrew Rybchenko }
24345e111ed8SAndrew Rybchenko }
24355e111ed8SAndrew Rybchenko }
24365e111ed8SAndrew Rybchenko
24375e111ed8SAndrew Rybchenko return (ENOTSUP);
24385e111ed8SAndrew Rybchenko }
24395e111ed8SAndrew Rybchenko
24405e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_test(__in efx_nic_t * enp)24415e111ed8SAndrew Rybchenko ef10_nvram_test(
24425e111ed8SAndrew Rybchenko __in efx_nic_t *enp)
24435e111ed8SAndrew Rybchenko {
24445e111ed8SAndrew Rybchenko efx_nvram_type_t type;
24455e111ed8SAndrew Rybchenko unsigned int npartns = 0;
24465e111ed8SAndrew Rybchenko uint32_t *partns = NULL;
24475e111ed8SAndrew Rybchenko size_t size;
24485e111ed8SAndrew Rybchenko unsigned int i;
24495e111ed8SAndrew Rybchenko efx_rc_t rc;
24505e111ed8SAndrew Rybchenko
24515e111ed8SAndrew Rybchenko /* Read available partitions from NVRAM partition map */
24525e111ed8SAndrew Rybchenko size = MC_CMD_NVRAM_PARTITIONS_OUT_TYPE_ID_MAXNUM * sizeof (uint32_t);
24535e111ed8SAndrew Rybchenko EFSYS_KMEM_ALLOC(enp->en_esip, size, partns);
24545e111ed8SAndrew Rybchenko if (partns == NULL) {
24555e111ed8SAndrew Rybchenko rc = ENOMEM;
24565e111ed8SAndrew Rybchenko goto fail1;
24575e111ed8SAndrew Rybchenko }
24585e111ed8SAndrew Rybchenko
24595e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_partitions(enp, (caddr_t)partns, size,
24605e111ed8SAndrew Rybchenko &npartns)) != 0) {
24615e111ed8SAndrew Rybchenko goto fail2;
24625e111ed8SAndrew Rybchenko }
24635e111ed8SAndrew Rybchenko
24645e111ed8SAndrew Rybchenko for (i = 0; i < npartns; i++) {
24655e111ed8SAndrew Rybchenko /* Check if the partition is supported for this port */
24665e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_to_type(enp, partns[i], &type)) != 0)
24675e111ed8SAndrew Rybchenko continue;
24685e111ed8SAndrew Rybchenko
24695e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_test(enp, partns[i])) != 0)
24705e111ed8SAndrew Rybchenko goto fail3;
24715e111ed8SAndrew Rybchenko }
24725e111ed8SAndrew Rybchenko
24735e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, size, partns);
24745e111ed8SAndrew Rybchenko return (0);
24755e111ed8SAndrew Rybchenko
24765e111ed8SAndrew Rybchenko fail3:
24775e111ed8SAndrew Rybchenko EFSYS_PROBE(fail3);
24785e111ed8SAndrew Rybchenko fail2:
24795e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
24805e111ed8SAndrew Rybchenko EFSYS_KMEM_FREE(enp->en_esip, size, partns);
24815e111ed8SAndrew Rybchenko fail1:
24825e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
24835e111ed8SAndrew Rybchenko return (rc);
24845e111ed8SAndrew Rybchenko }
24855e111ed8SAndrew Rybchenko
24865e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_DIAG */
24875e111ed8SAndrew Rybchenko
24885e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
24895e111ed8SAndrew Rybchenko ef10_nvram_partn_get_version(
24905e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
24915e111ed8SAndrew Rybchenko __in uint32_t partn,
24925e111ed8SAndrew Rybchenko __out uint32_t *subtypep,
24935e111ed8SAndrew Rybchenko __out_ecount(4) uint16_t version[4])
24945e111ed8SAndrew Rybchenko {
24955e111ed8SAndrew Rybchenko efx_rc_t rc;
24965e111ed8SAndrew Rybchenko
24975e111ed8SAndrew Rybchenko /* FIXME: get highest partn version from all ports */
24985e111ed8SAndrew Rybchenko /* FIXME: return partn description if available */
24995e111ed8SAndrew Rybchenko
25005e111ed8SAndrew Rybchenko if ((rc = efx_mcdi_nvram_metadata(enp, partn, subtypep,
25015e111ed8SAndrew Rybchenko version, NULL, 0)) != 0)
25025e111ed8SAndrew Rybchenko goto fail1;
25035e111ed8SAndrew Rybchenko
25045e111ed8SAndrew Rybchenko return (0);
25055e111ed8SAndrew Rybchenko
25065e111ed8SAndrew Rybchenko fail1:
25075e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
25085e111ed8SAndrew Rybchenko
25095e111ed8SAndrew Rybchenko return (rc);
25105e111ed8SAndrew Rybchenko }
25115e111ed8SAndrew Rybchenko
25125e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_rw_start(__in efx_nic_t * enp,__in uint32_t partn,__out size_t * chunk_sizep)25135e111ed8SAndrew Rybchenko ef10_nvram_partn_rw_start(
25145e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
25155e111ed8SAndrew Rybchenko __in uint32_t partn,
25165e111ed8SAndrew Rybchenko __out size_t *chunk_sizep)
25175e111ed8SAndrew Rybchenko {
25185e111ed8SAndrew Rybchenko efx_nvram_info_t eni = { 0 };
25195e111ed8SAndrew Rybchenko efx_rc_t rc;
25205e111ed8SAndrew Rybchenko
25215e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_info(enp, partn, &eni)) != 0)
25225e111ed8SAndrew Rybchenko goto fail1;
25235e111ed8SAndrew Rybchenko
25245e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_lock(enp, partn)) != 0)
25255e111ed8SAndrew Rybchenko goto fail2;
25265e111ed8SAndrew Rybchenko
25275e111ed8SAndrew Rybchenko if (chunk_sizep != NULL)
25285e111ed8SAndrew Rybchenko *chunk_sizep = eni.eni_write_size;
25295e111ed8SAndrew Rybchenko
25305e111ed8SAndrew Rybchenko return (0);
25315e111ed8SAndrew Rybchenko
25325e111ed8SAndrew Rybchenko fail2:
25335e111ed8SAndrew Rybchenko EFSYS_PROBE(fail2);
25345e111ed8SAndrew Rybchenko fail1:
25355e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
25365e111ed8SAndrew Rybchenko
25375e111ed8SAndrew Rybchenko return (rc);
25385e111ed8SAndrew Rybchenko }
25395e111ed8SAndrew Rybchenko
25405e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
ef10_nvram_partn_rw_finish(__in efx_nic_t * enp,__in uint32_t partn,__out_opt uint32_t * verify_resultp)25415e111ed8SAndrew Rybchenko ef10_nvram_partn_rw_finish(
25425e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
25435e111ed8SAndrew Rybchenko __in uint32_t partn,
25445e111ed8SAndrew Rybchenko __out_opt uint32_t *verify_resultp)
25455e111ed8SAndrew Rybchenko {
25465e111ed8SAndrew Rybchenko efx_rc_t rc;
25475e111ed8SAndrew Rybchenko
25485e111ed8SAndrew Rybchenko if ((rc = ef10_nvram_partn_unlock(enp, partn, verify_resultp)) != 0)
25495e111ed8SAndrew Rybchenko goto fail1;
25505e111ed8SAndrew Rybchenko
25515e111ed8SAndrew Rybchenko return (0);
25525e111ed8SAndrew Rybchenko
25535e111ed8SAndrew Rybchenko fail1:
25545e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
25555e111ed8SAndrew Rybchenko
25565e111ed8SAndrew Rybchenko return (rc);
25575e111ed8SAndrew Rybchenko }
25585e111ed8SAndrew Rybchenko
25595e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_NVRAM */
25605e111ed8SAndrew Rybchenko
25615e111ed8SAndrew Rybchenko #endif /* EFX_OPTS_EF10() */
2562