Index: head/sys/dev/sfxge/common/efx.h =================================================================== --- head/sys/dev/sfxge/common/efx.h +++ head/sys/dev/sfxge/common/efx.h @@ -1422,6 +1422,29 @@ #if EFSYS_OPT_BOOTCFG +/* Report size and offset of bootcfg sector in NVRAM partition. */ +extern __checkReturn efx_rc_t +efx_bootcfg_sector_info( + __in efx_nic_t *enp, + __in uint32_t pf, + __out_opt uint32_t *sector_countp, + __out size_t *offsetp, + __out size_t *max_sizep); + +/* + * Copy bootcfg sector data to a target buffer which may differ in size. + * Optionally corrects format errors in source buffer. + */ +extern efx_rc_t +efx_bootcfg_copy_sector( + __in efx_nic_t *enp, + __inout_bcount(sector_length) + uint8_t *sector, + __in size_t sector_length, + __out_bcount(data_size) uint8_t *data, + __in size_t data_size, + __in boolean_t handle_format_errors); + extern efx_rc_t efx_bootcfg_read( __in efx_nic_t *enp, Index: head/sys/dev/sfxge/common/efx_bootcfg.c =================================================================== --- head/sys/dev/sfxge/common/efx_bootcfg.c +++ head/sys/dev/sfxge/common/efx_bootcfg.c @@ -42,17 +42,24 @@ */ #define BOOTCFG_MAX_SIZE 0x1000 +/* Medford per-PF bootcfg sector */ +#define BOOTCFG_PER_PF 0x800 +#define BOOTCFG_PF_COUNT 16 + #define DHCP_END ((uint8_t)0xff) #define DHCP_PAD ((uint8_t)0) -/* Report size and offset of bootcfg sector in NVRAM partition. */ -static __checkReturn efx_rc_t -efx_bootcfg_sector( +/* Report the layout of bootcfg sectors in NVRAM partition. */ + __checkReturn efx_rc_t +efx_bootcfg_sector_info( __in efx_nic_t *enp, + __in uint32_t pf, + __out_opt uint32_t *sector_countp, __out size_t *offsetp, __out size_t *max_sizep) { + uint32_t count; size_t max_size; size_t offset; int rc; @@ -62,6 +69,7 @@ case EFX_FAMILY_SIENA: max_size = BOOTCFG_MAX_SIZE; offset = 0; + count = 1; break; #endif /* EFSYS_OPT_SIENA */ @@ -69,16 +77,20 @@ case EFX_FAMILY_HUNTINGTON: max_size = BOOTCFG_MAX_SIZE; offset = 0; + count = 1; break; #endif /* EFSYS_OPT_HUNTINGTON */ #if EFSYS_OPT_MEDFORD case EFX_FAMILY_MEDFORD: { - efx_nic_cfg_t *encp = &(enp->en_nic_cfg); - /* Shared partition (array indexed by PF) */ - max_size = 0x0800; - offset = max_size * encp->enc_pf; + max_size = BOOTCFG_PER_PF; + count = BOOTCFG_PF_COUNT; + if (pf >= count) { + rc = EINVAL; + goto fail2; + } + offset = max_size * pf; break; } #endif /* EFSYS_OPT_MEDFORD */ @@ -90,11 +102,17 @@ } EFSYS_ASSERT3U(max_size, <=, BOOTCFG_MAX_SIZE); + if (sector_countp != NULL) + *sector_countp = count; *offsetp = offset; *max_sizep = max_size; return (0); +#if EFSYS_OPT_MEDFORD +fail2: + EFSYS_PROBE(fail2); +#endif fail1: EFSYS_PROBE1(fail1, efx_rc_t, rc); return (rc); @@ -104,7 +122,7 @@ static __checkReturn uint8_t efx_bootcfg_csum( __in efx_nic_t *enp, - __in_bcount(size) caddr_t data, + __in_bcount(size) uint8_t const *data, __in size_t size) { _NOTE(ARGUNUSED(enp)) @@ -120,7 +138,7 @@ static __checkReturn efx_rc_t efx_bootcfg_verify( __in efx_nic_t *enp, - __in_bcount(size) caddr_t data, + __in_bcount(size) uint8_t const *data, __in size_t size, __out_opt size_t *usedp) { @@ -183,6 +201,95 @@ return (rc); } +/* + * Copy bootcfg sector data to a target buffer which may differ in size. + * Optionally corrects format errors in source buffer. + */ + efx_rc_t +efx_bootcfg_copy_sector( + __in efx_nic_t *enp, + __inout_bcount(sector_length) + uint8_t *sector, + __in size_t sector_length, + __out_bcount(data_size) uint8_t *data, + __in size_t data_size, + __in boolean_t handle_format_errors) +{ + size_t used_bytes; + efx_rc_t rc; + + /* Verify that the area is correctly formatted and checksummed */ + rc = efx_bootcfg_verify(enp, sector, sector_length, + &used_bytes); + + if (!handle_format_errors) { + if (rc != 0) + goto fail1; + + if ((used_bytes < 2) || + (sector[used_bytes - 1] != DHCP_END)) { + /* Block too short, or DHCP_END missing */ + rc = ENOENT; + goto fail2; + } + } + + /* Synthesize empty format on verification failure */ + if (rc != 0 || used_bytes == 0) { + sector[0] = 0; + sector[1] = DHCP_END; + used_bytes = 2; + } + EFSYS_ASSERT(used_bytes >= 2); /* checksum and DHCP_END */ + EFSYS_ASSERT(used_bytes <= sector_length); + EFSYS_ASSERT(sector_length >= 2); + + /* + * Legacy bootcfg sectors don't terminate with a DHCP_END character. + * Modify the returned payload so it does. + * Reinitialise the sector if there isn't room for the character. + */ + if (sector[used_bytes - 1] != DHCP_END) { + if (used_bytes >= sector_length) { + sector[0] = 0; + used_bytes = 1; + } + sector[used_bytes] = DHCP_END; + ++used_bytes; + } + + /* + * Verify that the target buffer is large enough for the + * entire used bootcfg area, then copy into the target buffer. + */ + if (used_bytes > data_size) { + rc = ENOSPC; + goto fail3; + } + memcpy(data, sector, used_bytes); + + /* Zero out the unused portion of the target buffer */ + if (used_bytes < data_size) + (void) memset(data + used_bytes, 0, data_size - used_bytes); + + /* + * The checksum includes trailing data after any DHCP_END character, + * which we've just modified (by truncation or appending DHCP_END). + */ + data[0] -= efx_bootcfg_csum(enp, data, data_size); + + return (0); + +fail3: + EFSYS_PROBE(fail3); +fail2: + EFSYS_PROBE(fail2); +fail1: + EFSYS_PROBE1(fail1, efx_rc_t, rc); + + return (rc); +} + efx_rc_t efx_bootcfg_read( __in efx_nic_t *enp, @@ -195,13 +302,20 @@ size_t sector_length; size_t sector_offset; efx_rc_t rc; + uint32_t sector_number; +#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD + sector_number = enp->en_nic_cfg.enc_pf; +#else + sector_number = 0; +#endif rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); if (rc != 0) goto fail1; /* The bootcfg sector may be stored in a (larger) shared partition */ - rc = efx_bootcfg_sector(enp, §or_offset, §or_length); + rc = efx_bootcfg_sector_info(enp, sector_number, + NULL, §or_offset, §or_length); if (rc != 0) goto fail2; @@ -232,17 +346,18 @@ if ((rc = efx_nvram_rw_start(enp, EFX_NVRAM_BOOTROM_CFG, NULL)) != 0) goto fail5; - rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, sector_offset, - (caddr_t)payload, sector_length); - - efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); - - if (rc != 0) + if ((rc = efx_nvram_read_chunk(enp, EFX_NVRAM_BOOTROM_CFG, + sector_offset, (caddr_t)payload, sector_length)) != 0) { + (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); goto fail6; + } + + if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) + goto fail7; /* Verify that the area is correctly formatted and checksummed */ rc = efx_bootcfg_verify(enp, (caddr_t)payload, sector_length, - &used_bytes); + &used_bytes); if (rc != 0 || used_bytes == 0) { payload[0] = (uint8_t)~DHCP_END; payload[1] = DHCP_END; @@ -274,7 +389,7 @@ */ if (used_bytes > size) { rc = ENOSPC; - goto fail7; + goto fail8; } if (sector_length > size) { memcpy(data, payload, used_bytes); @@ -293,6 +408,8 @@ return (0); +fail8: + EFSYS_PROBE(fail8); fail7: EFSYS_PROBE(fail7); fail6: @@ -326,13 +443,21 @@ size_t sector_offset; size_t used_bytes; efx_rc_t rc; + uint32_t sector_number; + +#if EFSYS_OPT_HUNTINGTON || EFSYS_OPT_MEDFORD + sector_number = enp->en_nic_cfg.enc_pf; +#else + sector_number = 0; +#endif rc = efx_nvram_size(enp, EFX_NVRAM_BOOTROM_CFG, &partn_length); if (rc != 0) goto fail1; /* The bootcfg sector may be stored in a (larger) shared partition */ - rc = efx_bootcfg_sector(enp, §or_offset, §or_length); + rc = efx_bootcfg_sector_info(enp, sector_number, + NULL, §or_offset, §or_length); if (rc != 0) goto fail2; @@ -396,16 +521,18 @@ goto fail10; if ((rc = efx_nvram_write_chunk(enp, EFX_NVRAM_BOOTROM_CFG, - 0, partn_data, partn_length)) != 0) { + 0, (caddr_t)partn_data, partn_length)) != 0) goto fail11; - } - efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); + if ((rc = efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG)) != 0) + goto fail12; EFSYS_KMEM_FREE(enp->en_esip, partn_length, partn_data); return (0); +fail12: + EFSYS_PROBE(fail12); fail11: EFSYS_PROBE(fail11); fail10: @@ -413,7 +540,7 @@ fail9: EFSYS_PROBE(fail9); - efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); + (void) efx_nvram_rw_finish(enp, EFX_NVRAM_BOOTROM_CFG); fail8: EFSYS_PROBE(fail8);